You're trying to mutate something while holding a shared reference to it somewhere else. This is a problem because Rust assumes things behind shared (&
) references will not be mutated, in order to prove your code is correct in multi-threaded contexts and in the presence of certain optimizations.
To fix it, you could tell the compiler to forget about multi-threading and skip those optimizations. One way to do that is using RefCell
:
use std::cell::RefCell;
#[derive(Debug, Clone)]
struct Movement {
x: i32,
y: i32,
}
fn main() {
let mut current = Some(RefCell::new(Movement { x: 1, y: 2 }));
// ^^^^^^^^^^^^ put it in a `RefCell`
let mut stack = Vec::new();
stack.push(¤t);
// note: as_ref, not as_mut
current.as_ref().unwrap().borrow_mut().x = 2;
println!("m: {:?}", current);
println!("stack.m: {:?}", stack.pop());
current = None;
}
If you need to share the object around different threads, you may want Mutex
or RwLock
instead. If the thing being shared is something trivially copiable, like a single i32
, you could use Cell<i32>
(non-thread-safe) or AtomicI32
(thread-safe). Collectively these are called interior mutability types because they allow mutation of the internal value through a shared reference.
But interior mutability only gets you so far. Putting references to local variables in a Vec
is not very flexible because the Vec
cannot outlive any of the variables that it references. A common idiom is to move objects into a Vec
and use indices, rather than built-in references, to access them. Here's one way to do that:
#[derive(Debug, Clone)]
struct Movement {
x: i32,
y: i32,
}
fn main() {
// this object will be moved into the vector
let temp = Some(Movement { x: 1, y: 2 });
let mut stack = Vec::new();
// now we set `current` to be the index of the object in the vector, and use
// `stack[current_index]` to refer to the object itself
let current_index = stack.len();
stack.push(temp);
// you could also do something like this to get a mutable reference, but it
// would prevent you from doing anything else with `stack` until you are done with
// `current` (`stack[current_index]` does not have this limitation):
//let current = stack.last_mut().unwrap()
stack[current_index].as_mut().unwrap().x = 2;
println!("m: {:?}", stack[current_index]);
println!("stack.m: {:?}", stack.pop());
}
There are more options. For instance, you could put the RefCell
in an Rc
(reference-counted smart pointer) and clone it to share the ownership between the Vec
and a local variable. In the absence of more information about your goals I would lean toward indexing the Vec
in order to avoid the complication of shared mutation, but the design is ultimately your choice.
See also