I am learning Rust and I've run into some confusing behaviour. The following code compiles fine and works as expected (edit: added code other than test function, previously omitted):
struct Container<'a> {
contents : &'a mut i32,
}
fn main() {
let mut one = Container { contents: &mut 5 };
test(&mut one);
println!("Contents: {}",one.contents);
}
fn test<'a>(mut x : &'a mut Container) {
*x.contents += 1;
let y = x;
*y.contents += 1;
x = y;
println!("{:?}",*x.contents)
}
Now in the statement
let y = x;
the type is inferred. Because x
is of type &'a mut Container
, I thought that this would be equivalent:
let y: &'a mut Container = x;
But when I do that, the compiler takes issue:
test_3.rs:25:5: 25:10 error: cannot assign to `x` because it is borrowed
test_3.rs:25 x = y;
^~~~~
test_3.rs:23:33: 23:34 note: borrow of `x` occurs here
test_3.rs:23 let y: &'a mut Container = x;
How is x
not borrowed by that point in the correctly working example? I tested by omitting the line x = y;
from the correctly working version and the compiler said:
test_3.rs:24:13: 24:14 note: `x` moved here because it has type `&mut Container<'_>`, which is moved by default
So I'm getting a move when I don't explicitly define the type but a borrow otherwise. What is going on, how do I get the same behavior as before while explicitly giving the type, and what is causing move behavior in one case but borrow in the other?
Edited with full program
Rust uses a borrow checker to enforce its ownership rules and ensure that programs are memory safe. The ownership rules dictate how Rust manages memory over the stack and heap. As you write Rust programs, you'll need to use variables without changing the ownership of the associated value.
What is Borrowing? When a function transfers its control over a variable/value to another function temporarily, for a while, it is called borrowing. This is achieved by passing a reference to the variable (& var_name) rather than passing the variable/value itself to the function.
When you do
let y = x;
a move happens. x
is emptied, so to speak, and ownership is transferred to y
.
When you do either of
let y: &mut _ = x;
let y: &'a mut _ = x;
x
is reborrowed to aid matching the lifetimes. This roughly translates to
let y: &mut _ = &mut *x;
let y: &'a mut _ = &mut *x;
This leaves x
non-empty, holding an aliased mutable borrow. Assigning to it thus must wait for y
to be destroyed. Alternatively, you can pre-move it
let tmp = x;
let y: &'a mut _ = tmp;
I'll admit it's nonobvious behaviour, and it's a shame that you can't borrow the contents of a value without borrowing the whole value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With