Reputation: 457
This code fails as expected at let c = a;
with compile error "use of moved value: a
":
fn main() {
let a: &mut i32 = &mut 0;
let b = a;
let c = a;
}
a is moved into b and is no longer available for an assignment to c. So far, so good.
However, if I just annotate b
's type and leave everything else alone:
fn main() {
let a: &mut i32 = &mut 0;
let b: &mut i32 = a;
let c = a;
}
the code fails again at let c = a;
But this time with a very different error message: "cannot move out of a
because it is borrowed ... borrow of *a
occurs here: let b: &mut i32 = a;
"
So, if I just annotate b
's type: no move of a
into b
, but instead a "re"-borrow of *a
?
What am I missing?
Cheers.
Upvotes: 15
Views: 1558
Reputation: 23294
To complement @Levans's answer on the specific question "Why does annotating the type change the behaviour?":
When you don't write the type, the compiler performs a simple move. When you do put the type, the let
statement becomes a coercion site as documented in "Coercion sites":
Possible coercion sites are:
- let statements where an explicit type is given.
In the present case the compiler performs a reborrow coercion, which is a special case of coercion going from &mut
to &mut
, as explained in this issue comment on GitHub.
Note that reborrowing in general and reborrow coercion in particular are currently poorly documented. There is an open issue on the Rust Reference to improve that point.
Upvotes: 3
Reputation: 15002
So, if I just annotate
b
's type: no move ofa
intob
, but instead a "re"-borrow of*a
?What am I missing?
Absolutely nothing, as in this case these two operations are semantically very similar (and equivalent if a
and b
belong to the same scope).
a
into b
, making a
a moved value, and no longer available.*a
in b
, making a
unusable as long as b
is in scope.The second case is less definitive than the first, you can show this by putting the line defining b
into a sub-scope.
This example won't compile because a
is moved:
fn main() {
let a: &mut i32 = &mut 0;
{ let b = a; }
let c = a;
}
But this one will, because once b
goes out of scope a
is unlocked:
fn main() {
let a: &mut i32 = &mut 0;
{ let b = &mut *a; }
let c = a;
}
Now, to the question "Why does annotating the type of b
change the behavior ?", my guess would be:
&mut _
into a &_
, or transforming a simple reference into a reference to a trait object). So the compiler opts for a re-borrow of the value, rather than a move.For example, this code is perflectly valid:
fn main() {
let a: &mut i32 = &mut 0;
let b: &i32 = a;
}
and here moving a
into b
would not make any sense, as they are of different type. Still this code compiles: b
simply re-borrows *a
, and the value won't be mutably available through a
as long as b
is in scope.
Upvotes: 10