String equality in Rust: how does referencing and dereferencing work? - string

As a Rust newbie, I'm working through the Project Euler problems to help me get a feel for the language. Problem 4 deals with palindromes, and I found two solutions for creating a vector of palindromes, but I'm not sure how either of them work.
I'm using a vector of strings, products, that's calculated like this:
let mut products = Vec::new();
for i in 100..500 {
for j in 500..1000 {
products.push((i * j).to_string());
}
}
For filtering these products to only those that are palindromic, I have the following two solutions:
Solution 1:
let palindromes: Vec<_> = products
.iter()
.filter(|&x| x == &x.chars().rev().collect::<String>())
.collect();
Solution 2:
let palindromes: Vec<_> = products
.iter()
.filter(|&x| *x == *x.chars().rev().collect::<String>())
.collect();
They both yield the correct result, but I have no idea why!
In Solution 1, we're comparing a reference of a string to a reference of a string we've just created?
In Solution 2, we dereference a reference to a string and compare it to a dereferenced new string?
What I would expect to be able to do:
let palindromes: Vec<_> = products
.iter()
.filter(|x| x == x.chars().rev().collect::<String>())
.collect();
I'm hoping somebody will be able to explain to me:
What is the difference is between my two solutions, and why do they both work?
Why can't I just use x without referencing or dereferencing it in my filter function?
Thank you!

Vec<String>.iter() returns an iterator over references (&String).
The closure argument of .filter() takes a reference to an iterator's item. So the type that is passed to the closure is a double reference &&String.
|&x| tells the closure to expect a reference, so x is now of type &String.
First solution: collect returns a String, of which & takes the reference. x is also a reference to a string, so the comparison is between two &String.
Second solution: The dereference operator * is applied to x, which results in a String. The right hand side is interesting: The String result of collect is dereferenced. This results in a string slice because String implements Deref<Target=str>. Now the comparison is between String and str, which is works because it is implemented in the standard library (Note that a == b is equivalent to a.eq(&b)).
Third solution: The compiler explains why it does not work.
the trait std::cmp::PartialEq<std::string::String> is not implemented for &&std::string::String
The left side is a double reference to string (&&String) and the right side is just a String . You need to get both sides to the same "reference level". All of these work:
x.iter().filter(|x| x == &&x.chars().rev().collect::<String>());
x.iter().filter(|x| *x == &x.chars().rev().collect::<String>());
x.iter().filter(|x| **x == x.chars().rev().collect::<String>());

Related

What is & doing in a rust for in loop? [duplicate]

This question already has an answer here:
What is the difference between `e1` and `&e2` when used as the for-loop variable?
(1 answer)
Closed 1 year ago.
Trying to understand how & works in a rust for..in loop...
For example let's say we have something simple like a find largest value function which takes a slice of i32's and returns the largest value.
fn largest(list: &[i32]) -> i32 {
let mut largest = list[0];
for item in list {
if *item > largest {
largest = *item;
}
}
largest
}
In the scenario given above item will be an &i32 which makes sense to me. We borrow a slice of i32's and as a result the item would also be a reference to the individual item in the slice. At this point we can dereference the value of item with * which is what I assume how a pointer based language would work.
But now if we alter this slightly below...
fn largest(list: &[i32]) -> i32 {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
If we put an & in front of item this changes item within the for..in into an i32... Why? In my mind this is completely counterintuitive to how I would have imagined it to work. This to me says, "Give me an address/reference to item"... Which in itself would already be a reference. So then how does item get dereferenced? Is this just a quirk with rust or am I fundamentally missing something here.
All variable assignments in Rust, including loop variables in for loops and function arguments, are assigned using pattern matching. The value that is being assigned is matched against the target pattern, and Rust tries to fill in the "blanks", i.e. the target variable names, in a way that substituting the values makes the pattern match the value. Let's look at a few examples.
let x = 5;
This is the simplest case. Obvious, substituting x with 5 makes both sides match.
if let Some(x) = Some(5) {}
Here, x will also become 5, since substituting that value into the pattern will make both side identical.
let &x = &5;
Again, the two sides match when setting x to 5.
if let (Some(&x), &Some(y)) = (Some(&5), &Some(6)) {}
This assignment results in x = 5 and y = 6, since substituting these values into the pattern makes both sides match.
Let's apply this to your for loop. In each loop iteration, the pattern after for is matched against the next value returned by the iterator. We are iterating an &[i32], and the item type of the resulting iterator is &i32, so the iterator yields a &i32 in each iteration. This reference is matched against the pattern &item. Applying what we have seen above, this means item becomes an i32.
Note that assigning a value of a type that does not have the Copy marker trait will move that value into the new variable. All examples above use integers, which are Copy, so the value is copied instead.
There is no magic here, pure logic. Consider this example:
let a = 1;
let b = &a; // b is a reference to a
let &c = &a; // c is a copy of value a
You can read the third line of the example above as "Assign reference to a to a reference to c". This basically creates a virtual variable "reference to c", assigns to it the value &a and then dereferences it to get the value of c.
let a = 1;
let ref_c = &a;
let c = *ref_c;
// If you try to go backwards into this assignments, you get:
let &c = &a;
let &(*ref_c) = &a;
let ref_c = &a; // which is exactly what it was
The same occurs with the for .. in syntax. You iterate over item_ref, but assign them to &item, which means that the type of item is Item.
for item_ref in list {
let item = *item_ref;
...
}
// we see that item_ref == &item, so above is same as
for &item in list {
...
}

How do references work in patterns in binding expressions? [duplicate]

This question already has answers here:
Meaning of '&variable' in arguments/patterns
(1 answer)
What is the difference between `e1` and `&e2` when used as the for-loop variable?
(1 answer)
What's the difference between ref and & when assigning a variable from a reference?
(3 answers)
Closed 3 years ago.
I came across below example in Rust book.
for &item in list.iter() {
if item > largest {
largest = item;
}
}
I suppose it means list.iter() returns the reference to the elements in the list hence &item but while comparing it with largest number why are we not using *item? Also, when I change the &item to item in the first line I am forced to use *item in 2nd and 3rd line by the compiler.
I have seen another example online.
(0..).map(|x| x * x)
.take_while(|&x| x <= limit)
.filter(|x| is_even(*x))
Here the closure in take_while accepts &x but uses x directly but the closure in filter takes x without reference but passes *x to is_even.
So how does this work in Rust?
What you are seeing here is called destructuring. This is a feature where you can take apart a structure with a pattern.
You probably already saw something like let (a, b) = returns_a_tuple();. Here, a tuple is destructured. You can also destructure references:
// The comments at the end of the line tell you the type of the variable
let i = 3; // : i32
let ref_i = &i; // : &i32
let ref_ref_i = &ref_i; // : &&i32
let &x = ref_i; // : i32
let &y = ref_ref_i; // : &i32
let &&z = ref_ref_i; // : i32
// All of these error because we try to destructure more layers of references
// than there are.
let &a = i;
let &&b = ref_i;
let &&&c = ref_ref_i;
This has the counter-intuitive effect that the more & you add in the pattern, the fewer & will the type of the variable have. But it does make sense in the context of destructuring: when you already mention the structure in the pattern, the structure won't be in the bound variables anymore.
(It is worth noting that this "destructuring references away" only works with referee types that are Copy. Otherwise you will get a "cannot move out of borrowed content" error.)
Now what does that have to do with your for loop and the closures? Turns out: patterns are everywhere. The slot between for and in in the for loop is a pattern, and arguments of functions and closures are pattern as well! This works:
// Destructuring a tuple in the for loop pattern
let v = vec![3];
for (i, elem) in v.iter().enumerate() {}
// Destructuring an array in the function argument (works the same for closures)
fn foo([x, y, z]: [f32; 3]) {}
I suppose it means list.iter() returns the reference to the elements in the list
Exactly.
... hence &item
"hence" is not correct here. The author of this code didn't want to work with the reference to the item, but instead work with the real value. So they added the & in the pattern to destructure the reference away.
but while comparing it with largest number why are we not using *item?
Yes, because the reference was already removed by the destructuring pattern.
Also, when I change the &item to item in the first line I am forced to use *item in 2nd and 3rd line by the compiler.
Yes, because now the pattern doesn't destructure the reference anymore, so item is a reference again. This is the basic gist with all of this: most of the time you can either remove the reference in the pattern by adding a & or you can remove the reference when using the variable by adding a *.
Here the closure in take_while accepts &x but uses x directly but the closure in filter takes x without reference but passes *x to is_even.
It should be clear by now why that is, right? The take_while closure removes the reference via destructuring in the pattern while the filter closure does it via standard dereferencing.
You can read more about all of this in this chapter of the book.

Why does the argument for the find closure need two ampersands?

I have been playing with Rust by porting my Score4 AI engine to it - basing the work on my functional-style implementation in OCaml. I specifically wanted to see how Rust fares with functional-style code.
The end result: It works, and it's very fast - much faster than OCaml. It almost touches the speed of imperative-style C/C++ - which is really cool.
There's a thing that troubles me, though — why do I need two ampersands in the last line of this code?
let moves_and_scores: Vec<_> = moves_and_boards
.iter()
.map(|&(column,board)| (column, score_board(&board)))
.collect();
let target_score = if maximize_or_minimize {
ORANGE_WINS
} else {
YELLOW_WINS
};
if let Some(killer_move) = moves_and_scores.iter()
.find(|& &(_,score)| score==target_score) {
...
I added them is because the compiler errors "guided" me to it; but I am trying to understand why... I used the trick mentioned elsewhere in Stack Overflow to "ask" the compiler to tell me what type something is:
let moves_and_scores: Vec<_> = moves_and_boards
.iter()
.map(|&(column,board)| (column, score_board(&board)))
.collect();
let () = moves_and_scores;
...which caused this error:
src/main.rs:108:9: 108:11 error: mismatched types:
expected `collections::vec::Vec<(u32, i32)>`,
found `()`
(expected struct `collections::vec::Vec`,
found ()) [E0308]
src/main.rs:108 let () = moves_and_scores;
...as I expected, moves_and_scores is a vector of tuples: Vec<(u32, i32)>. But then, in the immediate next line, iter() and find() force me to use the hideous double ampersands in the closure parameter:
if let Some(killer_move) = moves_and_scores.iter()
.find(|& &(_,score)| score==target_score) {
Why does the find closure need two ampersands? I could see why it may need one (pass the tuple by reference to save time/space) but why two? Is it because of the iter? That is, is the iter creating references, and then find expects a reference on each input, so a reference on a reference?
If this is so, isn't this, arguably, a rather ugly design flaw in Rust?
In fact, I would expect find and map and all the rest of the functional primitives to be parts of the collections themselves. Forcing me to iter() to do any kind of functional-style work seems burdensome, and even more so if it forces this kind of "double ampersands" in every possible functional chain.
I am hoping I am missing something obvious - any help/clarification most welcome.
This here
moves_and_scores.iter()
gives you an iterator over borrowed vector elements. If you follow the API doc what type this is, you'll notice that it's just the iterator for a borrowed slice and this implements Iterator with Item=&T where T is (u32, i32) in your case.
Then, you use find which takes a predicate which takes a &Item as parameter. Sice Item already is a reference in your case, the predicate has to take a &&(u32, i32).
pub trait Iterator {
...
fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
where P: FnMut(&Self::Item) -> bool {...}
... ^
It was probably defined like this because it's only supposed to inspect the item and return a bool. This does not require the item being passed by value.
If you want an iterator over (u32, i32) you could write
moves_and_scores.iter().cloned()
cloned() converts the iterator from one with an Item type &T to one with an Item type T if T is Clone. Another way to do it would be to use into_iter() instead of iter().
moves_and_scores.into_iter()
The difference between the two is that the first option clones the borrowed elements while the 2nd one consumes the vector and moves the elements out of it.
By writing the lambda like this
|&&(_, score)| score == target_score
you destructure the "double reference" and create a local copy of the i32. This is allowed since i32 is a simple type that is Copy.
Instead of destructuring the parameter of your predicate you could also write
|move_and_score| move_and_score.1 == target_score
because the dot operator automatically dereferences as many times as needed.

Value and references when providing a closure to Iterator::find

I have still quite a long way to go in learning Rust, but I find the way values and references are used to be inconsistent. This may be born from my own ignorance of the language.
For example, this works:
let x = (1..100).find(|a| a % 2 == 0);
But let x = (1..100).find(|a| a > 50); does not. I am not sure - why though?
Using let x = (1..100).find(|&a| a > 50); fixes the error, but then I thought using &a is like asking for reference of element from the range and hence following should work, but it does not:
let x = (1..100).find(|&a| *a > 50);
Again no idea why!
but then I thought using &a is like asking for reference of element from the range
This is the wrong part of your reasoning. Using & in pattern does exactly the opposite - it implicitly dereferences the matched value:
let &a = &10;
// a is 10, not &10 or &&10
As you probably already know, find() accepts a closure which satisfies FnMut(&T) -> bool, that is, this closure accepts a reference to each element of the iterator, so if you write (1..100).find(|a| ...), a will be of type &i32.
let x = (1..100).find(|a| a % 2 == 0) works because arithmetic operators are overloaded to work on references, so you can apply % to a reference and it still would be able to compile.
Comparison operators are not overloaded to handle references, and so you need to get an i32 from &i32. This could be done in two ways, first, like you already did:
let x = (1..100).find(|&a| a > 50)
Here we use & patterns to implicitly dereference the function argument. It is equivalent to this one:
let x = (1..100).find(|a| { let a = *a; a > 50 })
Another way would be to dereference the argument explicitly:
let x = (1..100).find(|a| *a > 50)
I thought using &a is like asking for reference of element from the range
Sometimes & is used as an operator, and sometimes it is used as a pattern match. For the closure parameter (|&a|), it is being used as a pattern match. This means that the variable a will be automatically dereferenced when it is used. It is also equivalent to do
let x = (1..100).find(|a| *a > 50);
Non-trivial patterns usually destructure something, i.e., break something into its components. This usually mirrors some construction syntax, so it looks very similar but is actually the inverse. This dualism applies to records, to tuples, to boxes (once those are properly implemented), and also to references:
The expression &x creates a reference to whatever x evaluates to. Here, the & turns a value of type T into one of type &T.
The pattern &a, on the other hand, eliminates the reference, so a is bound to what is behind the reference (note that a could also be another, more complicated pattern). Here, the & goes from a &T value to a T value.
The closures in your examples are all of of type &i32 -> bool1. So they accept a reference to an integer, and you can either work with that reference (which you do in the first example, which works because arithmetic operators are overloaded for references too) or you can use the pattern &a. In the latter case, a is a i32 (compare the general explanation above, substitute i32 for T), so of course you can't dereference it further.
1 This is not actually a real type, but it's close enough for our purposes.

String append, cannot move out of dereference of '&'pointer

I'm having trouble combining two strings, I'm very new to rust so If there is an easier way to do this please feel free to show me.
My function loops through a vector of string tuples (String,String), what I want to do is be able to combine these two strings elements into one string. Here's what I have:
for tup in bmp.bitmap_picture.mut_iter() {
let &(ref x, ref y) = tup;
let res_string = x;
res_string.append(y.as_slice());
}
but I receive the error : error: cannot move out of dereference of '&'-pointer for the line: res_string.append(y.as_slice());
I also tried res_string.append(y.clone().as_slice()); but the exact same error happened, so I'm not sure if that was even right to do.
The function definition of append is:
fn append(self, second: &str) -> String
The plain self indicates by-value semantics. By-value moves the receiver into the method, unless the receiver implements Copy (which String does not). So you have to clone the x rather than the y.
If you want to move out of a vector, you have to use move_iter.
There are a few other improvements possible as well:
let string_pairs = vec![("Foo".to_string(),"Bar".to_string())];
// Option 1: leave original vector intact
let mut strings = Vec::new();
for &(ref x, ref y) in string_pairs.iter() {
let string = x.clone().append(y.as_slice());
strings.push(string);
}
// Option 2: consume original vector
let strings: Vec<String> = string_pairs.move_iter()
.map(|(x, y)| x.append(y.as_slice()))
.collect();
It seems like you might be confusing append, which takes the receiver by value and returns itself, with push_str, which simply mutates the receiver (passed by mutable reference) as you seem to expect. So the simplest fix to your example is to change append to push_str. You'll also need to change "ref x" to "ref mut x" so it can be mutated.

Resources