What's the difference between ref and & when assigning a variable from a reference?
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:
- Borrowing
vec
into a temporary - Attempting to move
vec
intoy
, which is forbidden becausevec
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.
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