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
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.