After typing this out I think its obvious to me why this is not aloud but posting it anyway.
TL;DR: In the following examples allowing a value to be moved out (not copied) of the mutable reference to the struct could allow several undesirable things from invalid memory no longer pointing to the correct place to memory leaks -- I believe anyway. Copying obviously solves the problem so values with a copy
trait would just work but may not be what you want with larger data structures..
In this example I have a simple struct with one field on called value
with a type of Box<i32>
. I chose Box
in this example because it recreates the issue.
These examples are contrived to highlight the issue not indicative of the actual problem I ran into - not looking for how to solve the issue but rather what specifically made it fail to compile and the rationale behind it.
The first example
// Simple struct who stores a heap allocated value
struct Store {
value: Box<i32>
}
impl Store {
// Very contrived function to demonstrate quetsion
// stores the new value returning the old box to the caller
//
// Failes to copmile and the root of this question - why?
fn set_value(&mut self, new_value: i32) -> Box<i32> {
// Can't move box on the mutable reference
//
// My assumption is that self.value is effectively now invalid memory
// until it is assigned in the next statement which while I know that
// it's not gauranteed safe.
let old = self.value;
// For example if I didn't set the value and instead did a return like so
//
// return Box::new(new_value);
//
// then the struct has invliad memory upon returning to the caller which is clearly
// not good and I believe this is why borrow checker is calling it out.
// reassiging immediatley here
self.value = Box::new(new_value);
// would like to return the box ("pointer") here
return old;
}
}
fn main() {
let mut s = Store {
// Initial value
value: Box::new(15)
};
// Trying to set a new value on the struct and get back the
// point to the heap allocated value
let old_value = s.set_value(30);
println!("{:?}", old_value);
println!("{:?}", s.value);
}
This yields the following error:
|
| let old = self.value;
| ^^^^^^^^^^
| |
| cannot move out of borrowed content
| help: consider borrowing here: `&self.value`
Shorter example with using just structs but same general pattern:
#[derive(Debug)]
struct Person {
age: i32
}
struct Store {
value: Box<Person>
}
impl Store {
fn set_value(&mut self, new_value: Person) -> Box<Person> {
let old = self.value;
self.value = Box::new(new_value);
return old;
}
}
fn main() {
let mut s = Store {
value: Box::new(Person{ age: 15 })
};
let old_value = s.set_value(Person{ age: 30 });
println!("{:?}", s.value);
println!("{:?}", old_value);
}
and yields the same compiler error
--> main.rs:12:15
|
| let old = self.value;
| ^^^^^^^^^^
| |
| cannot move out of borrowed content
| help: consider borrowing here: `&self.value`
As I reason through this I think the answer is obvious why this is not allowed. If the value could be moved from self
in set_value
then upon return the variable s
could potentially have invalid memory that should not be trusted on the field value
because there is no guarantee that it will be set in that function.
My understanding anyway and could imagine there is a whole host of other potentially very bad things that could happen. Any expansion on this would be appreciated or correction to my understanding.