Trying to compile this program I get stuck on the borrow checker:
use std::collections::BTreeMap;
type Object<'a> = BTreeMap<&'a str, i32>;
struct Root<'a>(Object<'a>);
struct Sub<'a>(&'a mut Object<'a>, &'a str);
impl<'a> Root<'a> {
fn borrow_mut(&'a mut self, data: &'a str) -> Sub<'a> {
Sub(&mut self.0, data)
}
fn borrow(&self) {
println!("{:?}", self.0);
}
}
fn main() {
let mut me = Root(Object::new());
{
me.borrow_mut("data!");
}
me.borrow();
}
(Playground)
I get:
error[E0502]: cannot borrow `me` as immutable because it is also borrowed as mutable
--> src/main.rs:24:5
|
22 | me.borrow_mut("data!");
| -- mutable borrow occurs here
23 | }
24 | me.borrow();
| ^^ immutable borrow occurs here
25 | }
| - mutable borrow ends here
It looks like the mutable borrow should end before me.borrow()
but the borrow checker insists that it ends when main
ends.
To quickly explain what I'm trying to accomplish:
You are running afoul of a lifetime issue.
There are multiple different lifetimes in your program:
type Object<'a> = BTreeMap<&'a str, i32>;
=> this is one&'a mut Object<'a>
=> there are up to TWO herestruct Sub<'a>(&'a mut Object<'a>, &'a str);
=> there are up to THREE hereThere is, apparently, no reason for the reference to Object<'a>
to have the same lifetime than the &str
inside the BTreeMap
. However, you told the compiler that you wanted both lifetimes to be the same!
When you write:
struct Sub<'a>(&'a mut Object<'a>, &'a str);
you are telling the compiler that:
&str
inside BTreeMap
Object<'_>
&str
accompanying the Object<'_>
are all one and the same.
You have over-constrained the requirements; and as a result no solution can satisfy them.
Adding one more degree of freedom is sufficient! We'll just make the lifetime of the reference to Object<'_>
different from the lifetime of those &str
floating around:
struct Sub<'a, 'b: 'a>(&'a mut Object<'b>, &'b str);
impl<'b> Root<'b> {
fn borrow_mut<'a>(&'a mut self, data: &'b str) -> Sub<'a, 'b> {
Sub(&mut self.0, data)
}
fn borrow(&self) {
println!("{:?}", self.0);
}
}
Note the subtle 'b: 'a
:
Object<'b>
contains a reference to something whose lifetime is 'b
Object<'b>
(denoted 'a
) must be SHORTER than 'b
(otherwise you have a reference to something dead?)Thus, we say that 'b
outlives 'a
with 'b: 'a
.
And that's it. Simply loosening the requirements allow the compiler to allow your code to compile.
Note that in general, if you find yourself writing something like &'a &'a str
you are doing it wrong. If you think about it, you will realize that in order to create a reference to something, it must first be. And therefore a reference to an object necessarily has a shorter lifetime than the object itself (ever so slightly).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With