Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I call a mutable method (and store the result) twice in a row?

Tags:

rust

In the following example:

struct SimpleMemoryBank {
    vec: Vec<Box<i32>>,
}

impl SimpleMemoryBank {
    fn new() -> SimpleMemoryBank {
        SimpleMemoryBank{ vec: Vec::new() }
    }

    fn add(&mut self, value: i32) -> &mut i32 {
        self.vec.push(Box::new(value));
        let last = self.vec.len() - 1;
        &mut *self.vec[last]
    }
}

fn main() {
    let mut foo = SimpleMemoryBank::new();

    // Works okay
    foo.add(1);
    foo.add(2);

    // Doesn't work: "cannot borrow `foo` as mutable more than once at a time"
    let one = foo.add(1);
    let two = foo.add(2);
}

add() can be called multiple times in a row, as long as I don't store the result of the function call. But if I store the result of the function (let one = ...), I then get the error:

problem.rs:26:15: 26:18 error: cannot borrow `foo` as mutable more than once at a time
problem.rs:26     let two = foo.add(2);
                            ^~~
problem.rs:25:15: 25:18 note: previous borrow of `foo` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `foo` until the borrow ends
problem.rs:25     let one = foo.add(1);
                            ^~~
problem.rs:27:2: 27:2 note: previous borrow ends here
problem.rs:17 fn main() {
...
problem.rs:27 }
              ^
error: aborting due to previous error

Is this a manifestation of issue #6393: borrow scopes should not always be lexical?

How can I work around this? Essentially, I want to add a new Box to the vector, and then return a reference to it (so the caller can use it).

like image 433
Cornstalks Avatar asked Jan 16 '15 05:01

Cornstalks


1 Answers

This is exactly the problem that Rust is designed to prevent you from causing. What would happen if you did:

let one = foo.add(1);
foo.vec.clear();
println!("{}", one);

Or what if foo.add worked by pushing the new value at the beginning of the vector? Bad things would happen! The main thing is that while you have a borrow out on a variable, you cannot mutate the variable any more. If you were able to mutate it, then you could potentially invalidate the memory underlying the borrow and then your program could do a number of things, the best case would be that it crashes.

Is this a manifestation of issue #6393: borrow scopes should not always be lexical?

Kind of, but not really. In this example, you never use one or two, so theoretically a non-lexical scope would allow it to compile. However, you then state

I want to add a new Box to the vector, and then return a reference to it (so the caller can use it)

Which means your real code wants to be

let one = foo.add(1);
let two = foo.add(2);
do_something(one);
do_something(two);

So the lifetimes of the variables would overlap.

In this case, if you just want a place to store variables that can't be deallocated individually, don't overlap with each other, and cannot be moved, try using a TypedArena:

extern crate arena;

use arena::TypedArena;

fn main() {
    let arena = TypedArena::new();
    let one = arena.alloc(1);
    let two = arena.alloc(2);

    *one = 3;
    println!("{}, {}", one, two);
}
like image 124
Shepmaster Avatar answered Sep 30 '22 06:09

Shepmaster