Consider following code
fn main() {
let s = (&&0,);
let (x,) = s; // &&i32
let (&y,) = s; // &i32
let (&&z,) = s; // i32
let t = &(&0,);
let (x,) = t; // &&i32
let (&y,) = t; // i32
let u = &&(0,);
let (x,) = u; // &i32
let (&y,) = u; // mismatched types expected type `{integer}` found reference `&_`
}
Could someone explain, why & pattern behaves differently in every case? I suppose it is tied somehow to match ergonomics, maybe some coercions come into play? But I can't wrap my head around it.
You are correct, this is due to match ergonomics. The first case should hopefully be self explanatory, but the second and third cases can be a bit counter-intuitive.
In the second case:
(x,) is a non-reference pattern (see the second example in the RFC). The t tuple reference is dereferenced, and x is bound as a ref as it also is a non-reference pattern. Note that t.0 was a reference to begin with, thus resulting in x being a double reference.
(&y,) is also a non-reference pattern. The t tuple is dereferenced again to a (&i32,). However, &y is a reference pattern being matched to a &i32 reference. Hence y is bound with move mode and is an i32.
In the third case:
Using the same reasoning as the second case, u is dereferenced via Deref coercion to an (i32,), and x, a non-reference pattern, is bound in ref mode. Hence x is an &i32.
Again with the same reasoning as the second case, u is dereferenced to an (i32,). The &y reference pattern is then matched to an i32, a non-reference, which causes an error.
Related
Consider following code
fn main() {
let s = (&&0,);
let (x,) = s; // &&i32
let (&y,) = s; // &i32
let (&&z,) = s; // i32
let t = &(&0,);
let (x,) = t; // &&i32
let (&y,) = t; // i32
let u = &&(0,);
let (x,) = u; // &i32
let (&y,) = u; // mismatched types expected type `{integer}` found reference `&_`
}
Could someone explain, why & pattern behaves differently in every case? I suppose it is tied somehow to match ergonomics, maybe some coercions come into play? But I can't wrap my head around it.
You are correct, this is due to match ergonomics. The first case should hopefully be self explanatory, but the second and third cases can be a bit counter-intuitive.
In the second case:
(x,) is a non-reference pattern (see the second example in the RFC). The t tuple reference is dereferenced, and x is bound as a ref as it also is a non-reference pattern. Note that t.0 was a reference to begin with, thus resulting in x being a double reference.
(&y,) is also a non-reference pattern. The t tuple is dereferenced again to a (&i32,). However, &y is a reference pattern being matched to a &i32 reference. Hence y is bound with move mode and is an i32.
In the third case:
Using the same reasoning as the second case, u is dereferenced via Deref coercion to an (i32,), and x, a non-reference pattern, is bound in ref mode. Hence x is an &i32.
Again with the same reasoning as the second case, u is dereferenced to an (i32,). The &y reference pattern is then matched to an i32, a non-reference, which causes an error.
What is the purpose of & in the code &i in list? If I remove the &, it produces an error in largest = i, since they have mismatched types (where i is &32 and i is i32). But how does &i convert i into i32?
fn largest(list: &[i32]) -> i32 {
println!("{:?}", list);
let mut largest = list[0];
for &i in list {
if i > largest {
largest = i;
}
}
largest
}
fn main() {
let hey = vec![1, 3, 2, 6, 90, 67, 788, 12, 34, 54, 32];
println!("The largest number is: {}", largest(&hey));
}
Playground
It seems like it is somehow dereferencing, but then why in the below code, is it not working?
fn main() {
let mut hey: i32 = 32;
let x: i32 = 2;
hey = &&x;
}
It says:
4 | hey = &&x;
| ^^^ expected i32, found &&i32
|
= note: expected type `i32`
found type `&&i32`
So normally when you use for i in list, the loop variable i would be of type &i32.
But when instead you use for &i in list, you are not dereferencing anything, but instead you are using pattern matching to explicitly destructure the reference and that will make i just be of type i32.
See the Rust docs about the for-loop loop variable being a pattern and the reference pattern that we are using here. See also the Rust By Example chapter on destructuring pointers.
An other way to solve this, would be to just keep i as it is and then comparing i to a reference to largest, and then dereferencing i before assigning to largest:
fn largest(list: &[i32]) -> i32 {
println!("{:?}", list);
let mut largest = list[0];
for i in list {
if i > &largest {
largest = *i;
}
}
largest
}
fn main() {
let mut hey: i32 = 32;
let x: i32 = 2;
hey = &&x;
}
This simply doesn't work, because here you are assigning hey, which is an i32, to a reference to a reference to an i32. This is quite unrelated to the pattern matching and destructuring in the loop variable case.
This is the effect of destructuring. I won't completely describe that feature here, but in short:
In many syntax contexts (let bindings, for loops, function arguments, ...) , Rust expects a "pattern". This pattern can be a simple variable name, but it can also contain some "destructuring elements", like &. Rust will then bind a value to this pattern. A simple example would be something like this:
let (a, b) = ('x', true);
On the right hand side there is a value of type (char, bool) (a tuple). This value is bound to the left hand pattern ((a, b)). As there is already a "structure" defined in the pattern (specifically, the tuple), that structure is removed and a und b bind to the tuple's elements. Thus, the type of a is char and the type of b is bool.
This works with a couple of structures, including arrays:
let [x] = [true];
Again, on the right side we have a value of type [bool; 1] (an array) and on the left side we have a pattern in the form of an array. The single array element is bound to x, meaning that the type of x is bool and not [bool; 1]!
And unsurprisingly, this also works for references!
let foo = 0u32;
let r = &foo;
let &c = &foo;
Here, foo has the type u32 and consequently, the expression &foo has the type &u32. The type of r is also &u32, as it is a simple let binding. The type of c is u32 however! That is because the "reference was destructured/removed" by the pattern.
A common misunderstanding is that syntax in patterns has exactly the opposite effect of what the same syntax would have in expressions! If you have a variable a of type [T; 1], then the expression [a] has the type [[T; 1]; 1] → it adds stuff. However, if you bind a to the pattern [c], then y has the type T → it removes stuff.
let a = [true]; // type of `a`: `[bool; 1]`
let b = [a]; // type of `b`: `[[bool; 1]; 1]`
let [c] = a; // type of `c`: `bool`
This also explains your question:
It seems like it is somehow dereferencing, but then why in the below code, it is not working?
fn main() {
let mut hey:i32 = 32;
let x:i32 = 2;
hey = &&x;
}
Because you use & on the expression side, where it adds a layer of references.
So finally about your loop: when iterating over a slice (as you do here), the iterator yields reference to the slice's elements. So in the case for i in list {}, i has the type &i32. But the assignment largest = i; requires a i32 on the right hand side. You can achieve this in two ways: either dereference i via the dereference operator * (i.e. largest = *i;) or destructure the reference in the loop pattern (i.e. for &i in list {}).
Related questions:
Iterating over a slice's values instead of references in Rust?
Why is & needed to destructure a list of tuples during iteration?
I'm trying to understand how & and ref correspond. Here's an example where I thought these were equivalent, but one works and the other doesn't:
fn main() {
let t = "
aoeu
aoeu
aoeu
a";
let ls = t.lines();
dbg!(ls.clone().map(|l| &l[..]).collect::<Vec<&str>>().join("\n")); // works
dbg!(ls.clone().map(|ref l| l[..]).collect::<Vec<&str>>().join("\n")); // doesn't work
dbg!(ls.clone().map(|ref l| &l[..]).collect::<Vec<&str>>().join("\n")); // works again!
}
From the docs:
// A `ref` borrow on the left side of an assignment is equivalent to
// an `&` borrow on the right side.
let ref ref_c1 = c;
let ref_c2 = &c;
println!("ref_c1 equals ref_c2: {}", *ref_c1 == *ref_c2);
What would the equivalent to |l| &l[..] be with |ref l|? How does it correspond to the assignment examples in the docs?
Taking a look at the docs page for Lines(The iterator adapter for producing lines from a str), we can see that the item produced by it is:
type Item = &'a str;
Therefore the following happens when trying to do the "doesn't work" version:
dbg!(ls.clone().map(|ref l| l[..]).collect::<Vec<&str>>().join("\n")); # doesn't work
//Can become:
let temp = ls
.clone()
.map(|ref l| l[..])
.collect::<Vec<&str>>()
.join("\n");
println!("{}", temp);
Here we can see a crucial problem. l if of type &&str (Which I will explain below) so indexing into it will create a str, which is unsized and therefore cannot be outside of a pointer of some sort.
Now, onto the real thing you wanted to learn: What a ref pattern does:
When doing pattern matching or destructuring via the let binding, the ref keyword can be used to take references to the fields of a struct/tuple.
What this does is the following:
When we have let ref x = y, we take a reference to y.
When pattern matching on something (Like in your closure arguments you showed) we have a slightly different effect: the value under the reference is moved into the scope and then taken reference to while exposing a way to take the value under the reference. For example:
fn foo(ref x: String) {}
let y: fn(String) = foo;
This works because what is essentially being done is this:
fn foo(x: String) {
let x: &String = &x;
}
So what ref x does is take ownership of x and produce a reference to it.
On the other hand
When we have let &x = y we move a value out of y.
This is the opposite of ref, in that we take ownership of the value under y if we can. For example:
let x = 2;
let y = &x;
let &z = y; //Ok, we're moving a `Copy` type
This is only ok for copy types though as though this isn't exactly the same as let x = *y which would work for owned Boxes.
What is the purpose of & in the code &i in list? If I remove the &, it produces an error in largest = i, since they have mismatched types (where i is &32 and i is i32). But how does &i convert i into i32?
fn largest(list: &[i32]) -> i32 {
println!("{:?}", list);
let mut largest = list[0];
for &i in list {
if i > largest {
largest = i;
}
}
largest
}
fn main() {
let hey = vec![1, 3, 2, 6, 90, 67, 788, 12, 34, 54, 32];
println!("The largest number is: {}", largest(&hey));
}
Playground
It seems like it is somehow dereferencing, but then why in the below code, is it not working?
fn main() {
let mut hey: i32 = 32;
let x: i32 = 2;
hey = &&x;
}
It says:
4 | hey = &&x;
| ^^^ expected i32, found &&i32
|
= note: expected type `i32`
found type `&&i32`
So normally when you use for i in list, the loop variable i would be of type &i32.
But when instead you use for &i in list, you are not dereferencing anything, but instead you are using pattern matching to explicitly destructure the reference and that will make i just be of type i32.
See the Rust docs about the for-loop loop variable being a pattern and the reference pattern that we are using here. See also the Rust By Example chapter on destructuring pointers.
An other way to solve this, would be to just keep i as it is and then comparing i to a reference to largest, and then dereferencing i before assigning to largest:
fn largest(list: &[i32]) -> i32 {
println!("{:?}", list);
let mut largest = list[0];
for i in list {
if i > &largest {
largest = *i;
}
}
largest
}
fn main() {
let mut hey: i32 = 32;
let x: i32 = 2;
hey = &&x;
}
This simply doesn't work, because here you are assigning hey, which is an i32, to a reference to a reference to an i32. This is quite unrelated to the pattern matching and destructuring in the loop variable case.
This is the effect of destructuring. I won't completely describe that feature here, but in short:
In many syntax contexts (let bindings, for loops, function arguments, ...) , Rust expects a "pattern". This pattern can be a simple variable name, but it can also contain some "destructuring elements", like &. Rust will then bind a value to this pattern. A simple example would be something like this:
let (a, b) = ('x', true);
On the right hand side there is a value of type (char, bool) (a tuple). This value is bound to the left hand pattern ((a, b)). As there is already a "structure" defined in the pattern (specifically, the tuple), that structure is removed and a und b bind to the tuple's elements. Thus, the type of a is char and the type of b is bool.
This works with a couple of structures, including arrays:
let [x] = [true];
Again, on the right side we have a value of type [bool; 1] (an array) and on the left side we have a pattern in the form of an array. The single array element is bound to x, meaning that the type of x is bool and not [bool; 1]!
And unsurprisingly, this also works for references!
let foo = 0u32;
let r = &foo;
let &c = &foo;
Here, foo has the type u32 and consequently, the expression &foo has the type &u32. The type of r is also &u32, as it is a simple let binding. The type of c is u32 however! That is because the "reference was destructured/removed" by the pattern.
A common misunderstanding is that syntax in patterns has exactly the opposite effect of what the same syntax would have in expressions! If you have a variable a of type [T; 1], then the expression [a] has the type [[T; 1]; 1] → it adds stuff. However, if you bind a to the pattern [c], then y has the type T → it removes stuff.
let a = [true]; // type of `a`: `[bool; 1]`
let b = [a]; // type of `b`: `[[bool; 1]; 1]`
let [c] = a; // type of `c`: `bool`
This also explains your question:
It seems like it is somehow dereferencing, but then why in the below code, it is not working?
fn main() {
let mut hey:i32 = 32;
let x:i32 = 2;
hey = &&x;
}
Because you use & on the expression side, where it adds a layer of references.
So finally about your loop: when iterating over a slice (as you do here), the iterator yields reference to the slice's elements. So in the case for i in list {}, i has the type &i32. But the assignment largest = i; requires a i32 on the right hand side. You can achieve this in two ways: either dereference i via the dereference operator * (i.e. largest = *i;) or destructure the reference in the loop pattern (i.e. for &i in list {}).
Related questions:
Iterating over a slice's values instead of references in Rust?
Why is & needed to destructure a list of tuples during iteration?
I've got the following code:
use std::collections::HashMap;
fn main() {
let xs: Vec<&str> = vec!("a", "b", "c", "d");
let ys: Vec<i32> = vec!(1, 2, 3, 4);
let mut map: HashMap<String,i32> = HashMap::new();
for (x,y) in xs.iter().zip(ys) {
map.insert(x.to_owned(), y);
}
println!("{:?}", map);
}
Which results in error:
<anon>:8:20: 8:32 error: mismatched types:
expected `collections::string::String`,
found `&str`
(expected struct `collections::string::String`,
found &-ptr) [E0308]
<anon>:8 map.insert(x.to_owned(), y);
But it doesn't make sense to me. x should be &&str at this point. So why doesn't &&str.to_owned() automagically Deref the same way x.to_string() does at this point? (Why is x.to_owned() a &str?)
I know I can fix this by either using x.to_string(), or xs.into_iter() instead.
Because ToOwned is implemented for T where T: Clone, and Clone is implemented for &T. You need to roughly understand how pattern matching on &self works when both T and &T are available. Using a pseudo-syntax for exposition,
str → String
str doesn't match &self
&str (auto-ref) matches &self with self == str
Thus ToOwned<str> kicks in.
&str → String
&str matches &self with self == str
Thus ToOwned<str> kicks in.
&&str → &str
&&str matches &self with self == &str
Thus ToOwned<&T> kicks in.
Note that in this case, auto-deref can never kick in, since &T will always match in cases where T might, which lowers the complexity a bit. Note also that auto-ref only kicks in once (and once more for each auto-deref'd type).
To copy from huon's much better answer than mine,
The core of the algorithm is:
For each each "dereference step" U (that is, set U = T and then U = *T, ...)
if there's a method bar where the receiver type (the type of self in the method) matches U exactly , use it (a "by value method")
otherwise, add one auto-ref (take & or &mut of the receiver), and, if some method's receiver matches &U, use it (an "autorefd method")
FWIW, .into() is normally prettier than .to_owned() (especially when types are implied; oft even when not), so I suggest that here. You still need a manual dereference, though.