struct Foo<'a> {
foo: &'a mut str
}
#[derive(Debug)]
struct Bar;
impl<'a> Foo<'a> {
fn get_thing(&'a mut self) -> Bar {
//Code modifying self.foo...
Bar
}
fn get_and_log(&'a mut self) -> Bar {
let res = self.get_thing();
format!("logging: {}", self.foo);
res
}
}
fn main() {
let mut s = "foo".to_string();
let mut foo = Foo { foo: &mut s };
format!("{:?}", foo.get_and_log());
}
I get this error:
error[E0502]: cannot borrow `self.foo` as immutable because `*self` is also borrowed as mutable
--> src/main.rs:18:32
|
17 | let res = self.get_thing();
| ---- mutable borrow occurs here
18 | format!("logging: {}", self.foo);
| ^^^^^^^^ immutable borrow occurs here
19 | res
20 | }
| - mutable borrow ends here
If I replace the &'a mut selfs by &mut selfs, the code compiles, but I can't in the real code as I need to access self.foo in get_thing.
The code seems it should be correct, and I don't understand why the mutable borrow gets dragged until the end of the function. Is there a way to get this to compile?
Maybe I'm misunderstanding the problem, but I have no trouble modifying self.foo inside get_thing, even if the reference is &mut self instead of &'a mut self:
struct Foo<'a> {
foo: &'a mut str
}
#[derive(Debug)]
struct Bar;
impl<'a> Foo<'a> {
fn get_thing(&mut self) -> Bar {
unsafe {
self.foo.as_bytes_mut()[0] = b'A';
}
Bar
}
fn get_and_log(&mut self) -> Bar {
let res = self.get_thing();
format!("logging: {}", self.foo);
res
}
}
fn main() {
let mut s = "foo".to_string();
let mut foo = Foo { foo: &mut s };
format!("{:?}", foo.get_and_log());
}
To wit, by taking &'a mut self, you require from the caller that they pass you a reference to Foo that lives at least as long as the reference Foo contains. But that is not necessary to modify self.foo. The reference to Foo may well live for a shorter time than the reference to str. After all, you don't intend to keep it around (I assume).
If I propagate the second lifetime to the Foo struct (like this: struct Foo<'a, 'b: 'a> {...}), I can remove the &'a mut selfs, and everything works correctly.
I suppose the self-referential lifetime in StackFrame was somehow "pinning it down" and making it invariant, and so forcing every borrow to have the same lifetime.
You're seen to be trying to use references as if they were pointers, but that generally doesn't work. It's more useful to think of mut references as temporary exclusive write locks on data.
You probably want to build trees from Box<StackFrame>, which is also a pointer, but is generally usable without crippling restrictions.
You can also use RefCell and use immutable references everywhere, and rely on RefCell to "cheat" mutability where needed.
That's exactly the way I use them!
When you construct a StackFrame, you borrow its parent, so that you are the only one who can modify it. As soon as you drop the frame, you can access its parent again.
My code wasn't compiling because I got the lifetimes wrong: the parent StackFrame must live longer than the current one, not exactly as long. When I fixed this, all the other lifetime annotations which shouldn't have been there simply disappeared, and now everything's working great!
Specifically because the reference is a mutable one - it makes types invariant. &'a mut StackFrame<'a> will not allow the StackFrame to outlive the 'a lifetime; in other words, you cannot substitute a longer lived StackFrame. If that was allowed, you could smuggle a shorter lived reference (ie 'a) into the longer lived StackFrame, leaving t with a dangling reference.