What is wrong with this code?
fn example() {
let vec = vec![1, 2, 3];
let &_y = &vec;
}
First of all, you do not need the & in your working example. If you use it, you end up with a &&Vec<_>, which you don't really need.
let vec = vec![1, 2, 3];
let ref y = vec;
The problem with your first code is that you are doing two things at once. Lets split it up into two:
The first part creates a reference to vec
let y1 = &vec;
The second part (the & before the variable binding), destructures.
let &y2 = y1;
This means you are trying to move out of the reference, which only works if the type is Copy, because then any attempt to move will copy the object instead.
The same symbol (&) is doing two different things when used on the right-end and left-end side of a binding. The left-hand side works like a pattern matching, so:
let x = (y, z); // x contains a tuple with value (y, z)
let (a, b) = x // x is destructured into (a, b), so now
// a has value y and b has value z
In the same way
let x = &y; // x is a reference to y
let &z = x; // this is like let &z= &y, so we want z to be y
// this is equivalent to let z = *x
A ref binding on the left side is saying "pattern match by reference, not by value". So these two statements are equivalent:
let ref y = vec;
let y = &vec;
although in a let, the second one is more idiomatic.
You can see more examples on the pointers/ref chapter on rust by example
Pattern binding can get some using to ;)
In order to understand what the compiler does, you can use the let _: () = ...; trick. By assigning to a variable of type (), you force the compiler to print an error message giving you the type it inferred for your variable.
In the first example:
let vec = vec![1, 2, 3];
let &y = &vec;
let _: () = y;
we get:
error[E0308]: mismatched types --> src/lib.rs:4:13 | 4 | let _: () = y; | ^ expected (), found struct `std::vec::Vec` | = note: expected type `()` found type `std::vec::Vec<{integer}>`
the type of y is Vec<i32>.
What it means is that you are:
vec into a temporaryvec into y, which is forbidden because vec is already borrowed.The equivalent correct code would be:
let vec = vec![1, 2, 3];
let y = vec;
In the second example:
let vec = vec![1, 2, 3];
let ref y = &vec;
let _: () = y;
we get:
error[E0308]: mismatched types --> src/lib.rs:4:17 | 4 | let _: () = y; | ^ expected (), found reference | = note: expected type `()` found type `&&std::vec::Vec<{integer}>`
Thus y is &&Vec<i32>.
This let us see that let ref a = b; is generally equivalent to let a = &b;, and therefore in this case: let y = &&vec;.
ref is made for destructuring; for example, if you had:
let vec = Some(vec![1, 2, 3]);
if let Some(ref y) = vec {
// use `y` here
}
you can use ref here to be able to bind y to &Vec<i32> without moving even though vec here has type Option<Vec<i32>>. Indeed, the purpose of ref is to take a reference inside an existing object during destructuring.
In general, in a let statement, you will not use ref.
And since Rust 1.26, ref is inferred in pattern matching; see the stabilization of match ergonomics.