Comparing value enclosed in RefCell<T> - rust

I have a structure with a field defined as follows:
log_str: RefCell<String>
I performed various calls to borrow_mut() to call push_str(.) on the field. At the end, I'm assessing its value using:
assert_eq!(os.log_str.borrow(), "<expected value>");
Nonetheless, the line of the assert raises a compile-time error with the message:
error[E0369]: binary operation == cannot be applied to type std::cell::Ref<'_, std::string::String>
I understand why the error is happening, since the compiler even hints:
an implementation of std::cmp::PartialEq might be missing for std::cell::Ref<'_, std::string::String>
My question is: how should I compare the value enclosed in a RefCell<T> (typically in this case, comparing the enclosed string with an expected value).
Thanks !

You want to de-reference the borrowed value:
assert_eq!(*os.log_str.borrow(), "<expected value>");

Related

Value moved in an empty expression statement

On my first day of fiddling with Rust, I tried to execute an empty expression statement. The compiler threw a "value borrowed here after move" error when I tried printing the variable used inside the empty expression statement.
Here is the sample code:
fn main() {
let tstr = String::from("test"); // "move occurs because `tstr` has type `String`, which does not implement the `Copy` trait"
tstr; // causes move? <- "value moved here"
println!("{}",tstr); // "value borrowed here after move"
}
Does the empty expression call some hidden trait of the String method which takes ownership of the object? Or is something else at play here?
Rust is generally considered to be an "expression oriented" language. Broadly speaking, this means that most language constructs are expressions. Expressions are things that evaluate to values, and critically, do so independent of the context that they appear in.
A concrete consequence of this is that whether you write
let foo = <expression>;
or
<expression>;
doesn't affect1 how Rust evaluates <expression>. In particular, it doesn't change whether evaluating <expression> causes a value to be moved.
Since evaluating the right-hand side of
let new_tstr = tstr;
clearly involves moving tstr, so to does evaluating
tstr;
1 At the language level. Rust may of course compile these statements differently so long as the observable behavior remains the same. See "as-if rule."

Convert Vec<Cow<'_, [u8]> to &str

A lib (quick_xml) function (attributes()) returns a value with a type Vec<Cow<'_, [u8]>.
The exact line is e.attributes().map(|a| a.unwrap().value).collect::<Vec<_>>() and the printed value = [[116, 101, 115, 116]].
How can I convert it to a string ("test" in this case) so I can use it later?
I assume you are referencing this example. In the future, please give us the whole source code – this makes answering the question much easier.
Understanding the code
Let's take it one step at a time:
e.attributes().map(|a| a.unwrap().value).collect::<Vec<_>>()
^
e is a BytesStart struct, so it represents an opening XML tag, in your case <tag1 att1 = "test">.
e.attributes().map(|a| a.unwrap().value).collect::<Vec<_>>()
^^^^^^^^^^^^
This is the attributes method of BytesStart. It returns the Attributes struct which represents the set of attributes that one tag has. In your case, that is only one attribute: It has the name attr1 and the value test.
Attributes is an iterator, this means you can iterate over the contained Attributes (note that Attributes contains multiple Attributes – these are not the same type!). If you want to learn more about iterators, you may want to read the chapter about it in the Rust book.
e.attributes().map(|a| a.unwrap().value).collect::<Vec<_>>()
^^^^^^^^^^^^^^^^^^^^^^^^^
Here, we call the map method of the Iterator struct. It lets us transform one iterator (in this case the Attributes struct) into another iterator by transforming each value of the iterator. We call it with a closure (if you don't know what this is, the Rust book also has a chapter about this) that takes one value of the original iterator and returns the transformed value of the new iterator. Now, let's look at that iterator:
|a| a.unwrap().value
^^^
This iterator takes one argument named a, which is, as I said above, the type that the original iterator contains. I said above that Attributes contains multiple Attributes – while this is true, it is not the full picture, the iterator iterates over Result<Attribute>, and that is the type of a.
|a| a.unwrap().value
^^^^^^^^^^
When operating normally, a will always be an instance of Result::Ok containing your Attribute, but if your XML is somehow invalid, amay also be a Result::Err to indicate some kind of parse error. We don't want to care about error handling here, so we just call the unwrap method of Result that returns the contained Argument and panics if there was an error.
|a| a.unwrap().value
^^^^^
The Attribute struct contains two values: key and [value]. We are only interested in value, so let's select it. The value field is of type Cow<'a, [u8]>. Cow is a smart pointer with some interesting properties that aren't really relevant here. If you want to learn more about it, you may be interested in the documentation of Cow (although his may be a bit too complicated for a Rust newbie). For the remainder of this explanation, I will just pretend value is of type &[u8] (a reference to a u8 slice).
We now have determined that the closure returns a &[u8], therefore the iterator returned by map iterates over &[u8].
e.attributes().map(|a| a.unwrap().value).collect::<Vec<_>>()
^^^^^^^^^^^^^^^^^^^
Now we call the collect method of Iterator which transforms the iterator into a collection. The type of the collection is given as a generic parameter an is Vec<_>. The underscore tells rustc to try to find out the correct type by context or output an error if this is not possible. The only type possible here is &[u8], therefore, this method returns a Vec<&[u8]>.
The solution
You can use the unescape_and_decode_value method of Attribute. This transforms the Attribute value to a String and also unescapes escape sequences if the attribute contains them.
e.attributes().map(|a| a.unwrap().unescape_and_decode_value(&reader).unwrap()).collect::<Vec<_>>()
Note that this still returns Vector<String>, not String. The vector contains the values of all attributes assigned to this element – in this case, it's just the attribute value "Test".
You can use std::str::from_utf8 for fallible conversion of &[u8] to &str:
use std::borrow::Cow;
fn main() {
let s = "test";
let v = s.as_bytes();
let c = Cow::Borrowed(v);
println!("{}", std::str::from_utf8(&*c).unwrap());
}
The crucial part is the deref and reborrow of Cow since from_utf8 takes &[u8] instead of Cow. Cow implements Deref for T, in this case T is [u8], thus you can get a &[u8] via &*.
Playground Link
In your concrete example you should be able to get a Vec<&str> by:
e.attributes().map(|a| std::str::from_utf8(&*a.unwrap().value).unwrap()).collect::<Vec<_>>()

Understanding Ok with error propagation operator?

I've seen this pattern on more than one occasion:
fn f() -> Result<..., ...> {
...
Ok(expression()?)
}
Specifically, I find the order Ok(expression()?) confusing. What's the return type of expression()? and is there an interplay between Ok and this type? It seems, the function Ok must do more than just capturing the value. But how can it force a return with an error, if it's the last expression wrapping the return type of expression()?.
The ? operator either returns (as in the return statement) the error variant, or extracts the value from the Ok variant. Thus Ok(expression()?) is more or less equal to:
// This pattern is so common in rust, that the language designers
// introduced the `?` to reduce the amount of boilerplate needed
let result = expression();
let r = match result{
Err(e) => return Err(e.into()), // tries to convert the error into the required type if necessary (and if possible)
Ok(r) => r
}
Ok(r)
So basically Ok(expression()?) can be simplified to just
expression() if its Err variant matches the one from the function definition.
Historical context:
Extracting the Ok() variant and propagating the Err variant is a very common pattern in rust. In order to reduce the amount of boilerplate code, the rust team introduced the try!() macro; But it's so clumsy and does not really work well with method chaining. The rust team introduced the ? operator, which is doing absolutely the same thing, except for it's not so intrusive as try!()
? just leverages the error returned by expression if it exists or unwraps the value from within the returned Ok if not. Ok(expression()?) could be just expresion() and clippy would warn you about that:
warning: question mark operator is useless here
--> src/lib.rs:8:5
|
8 | Ok(expression()?)
| ^^^^^^^^^^^^^^^^^ help: try: `expression()`
|
= note: `#[warn(clippy::needless_question_mark)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
Playground
You can follow the documentation for further information
As per the comments. Notice that ? is useful if the error types differ but you have an implementation of From between error types or you use Box<dyn Error> as return type.
In which case you can either, bubble up the error if it is an error, otherwise rewrap the value with an Ok
Ok(expression()?)
or, map your error type into the correct error type with map_err
expression.map_err(|e| ...)

Rust - the trait `StdError` is not implemented for `OsString`

I'm writing some Rust code which uses the ? operator. Here is a few lines of that code:
fn files() -> Result<Vec<std::string::String>, Box<Error>> {
let mut file_paths: Vec<std::string::String> = Vec::new();
...
file_paths.push(pathbuf.path().into_os_string().into_string()?);
...
Ok(file_paths)
}
However, even though I'm using ? on a Result it is giving me the following error:
`the trait `StdError` is not implemented for `OsString`.
This is contrary to the Rust documentation here, which states that:
The ? is shorthand for the entire match statements we wrote earlier. In other words, ? applies to a Result value, and if it was an Ok, it unwraps it and gives the inner value. If it was an Err, it returns from the function you're currently in.
I've confirmed that pathbuf.path().into_os_string().into_string() is of type Result, because when I remove the ?, I get the following compiler error:
expected struct `std::string::String`, found enum `std::result::Result`
(since file_paths is a Vector of strings, not Results).
Is this a bug with the Rust language or documentation?
In fact I tried this without pushing to the Vector, but simply initializing a variable with the value of pathbuf.path().into_os_string().into_string()?, and I got the same error.
The function OsString::into_string is a little unusual. It returns a Result<String, OsString> - so the Err variant is actually not an error.
In the event that the OsString cannot be converted into a regular string, then the Err variant is returned, containing the original string.
Unfortunately this means you cannot use the ? operator directly. However, you can use map_err to map the error variant into an actual error, like this:
file_paths.push(
pathbuf.path()
.into_os_string()
.into_string().
.map_err(|e| InvalidPathError::new(e))?
);
In the above example, InvalidPathError might be your own error type. You could also use an error type from the std library.

How can I return a String Vector?

How can I return a String Vector in Rust? I tried:
fn test_vec() -> Vec<&str> {
vec!("foo", "bar")
}
The compiler says something about lifetimes, but I'm not sure my problem is really about lifetimes:
src/main.rs:9:22: 9:26 error: missing lifetime specifier [E0106]
I'm a bit lost, I think I misunderstood (or forgot to learn) something.
A &str is not a String. It is a "string slice", meaning a kind of pointer into a String or something equivalent that is stored somewhere else. In your case you are using string literals (using quotes gives you string literals). String literals are of the type &'static str, because they are stored in the same place where the compiled code is stored and thus are available for the 'static lifetime, which means for (at least) the entire runtime of your program.
So the easy fix is to have your method return that specific type: &'static str.
The compiler cannot infer a lifetime for the returned reference, because your function does not take any arguments of reference type. The only way the compiler will infer a lifetime in a function's signature, is by assuming that if you are returning a reference, it needs to live shorter than the argument it's referring to. There's more information in The Book

Resources