Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type inference and borrowing vs ownership transfer

Tags:

rust

ownership

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

like image 308
Mark Mywords Avatar asked Oct 11 '15 16:10

Mark Mywords


People also ask

How rust manages memory using ownership and borrowing?

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 in Rust?

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.


1 Answers

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.

like image 97
Veedrac Avatar answered Oct 12 '22 05:10

Veedrac