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:
x
without referencing or dereferencing it in my filter function?Thank you!
We can use the eq(), eq_ignore_ascii_case() and == to compare strings in Rust.
In general, &* means to first dereference ( * ) and then reference ( & ) a value. In many cases, this would be silly, as we'd end up at the same thing. However, Rust has deref coercions. Combined with the Deref and DerefMut traits, a type can dereference to a different type!
Rust owned String type, the string itself lives on the heap and therefore is mutable and can alter its size and contents.
Vec<String>.iter()
returns an iterator over references (&String
)..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>());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With