Why does the following code compiles?
I would expect the Rust compiler to tell me
"borrowed value does not live long enough" when setting the reference
(store.barber.set(Some(&barber));
Since barber has a shorter lifetime than shop.
use core::cell::Cell;
struct Shop<'a> {
barber: Cell<Option<&'a Barber<'a>>>,
}
struct Barber<'a> {
shop: Cell<Option<&'a Shop<'a>>>,
}
fn main() {
let shop = Shop { barber: Cell::new(None) };
{
let barber = Barber { shop: Cell::new(Some(&shop))};
shop.barber.set(Some(&barber));
}
}
Playground
My assumption is confirmed by @jmb with his answer (here).
The code example comes from here. But why the whole thing works or where my misunderstanding lies is still unclear to me.
The code which I commented to on @Netwaves answer.
In case the link don't work anymore and also to clarify the question.
The inner scope was just to make the lifetime more clear.
A usage would look more like this:
use core::cell::Cell;
struct Shop<'a> {
barber: Cell<Option<&'a Barber<'a>>>,
shop_state: Cell<bool>,
}
impl<'a> Shop<'a> {
fn change_barber_state(&self) {
self.barber.get().unwrap().change_state();
}
fn change_state(&self) {
self.shop_state.set(!self.shop_state.get());
}
}
struct Barber<'a> {
shop: Cell<Option<&'a Shop<'a>>>,
barber_state: Cell<bool>,
}
impl<'a> Barber<'a> {
fn change_state(&self) {
self.barber_state.set(!self.barber_state.get());
}
fn change_shop_state(&self) {
self.shop.get().unwrap().change_state();
}
}
fn main() {
let shop = Shop {
barber: Cell::new(None),
shop_state: Cell::new(false),
};
let barber = Barber {
shop: Cell::new(Some(&shop)),
barber_state: Cell::new(false),
};
shop.barber.set(Some(&barber));
println!("{:?}", barber.barber_state);
println!("{:?}", shop.shop_state);
shop.change_barber_state();
barber.change_shop_state();
println!("{:?}", barber.barber_state);
println!("{:?}", shop.shop_state);
}
Is the lifetime the same through the same scope?
I thought the lifetime is also given by freeing the resources, which happens in reverse order to the declaration. Or is this only relevant if the drop trait is implemented?
Why does this mutual referencing structures work in Rust with specified lifetimes?
It does because as per your code it actually lives long enough.
The problem (no problem) is that you are not using shop
anymore after the inner scope, so the compiler is smart enough to say nothing is wrong with your program.
But if you add an access he will start complaining, and with a good reason:
fn main() {
let shop = Shop { barber: Cell::new(None) };
{
let barber = Barber { shop: Cell::new(Some(&shop))};
shop.barber.set(Some(&barber));
}
shop.barber.get();
}
fails to compile with:
error[E0597]: `barber` does not live long enough
--> src/main.rs:15:30
|
15 | shop.barber.set(Some(&barber));
| ^^^^^^^ borrowed value does not live long enough
16 | }
| - `barber` dropped here while still borrowed
17 | shop.barber.get();
| ----------------- borrow later used here
Playground
fn main() {
let shop = Shop {
barber: Cell::new(None),
shop_state: Cell::new(false),
};
let barber = Barber {
shop: Cell::new(Some(&shop)),
barber_state: Cell::new(false),
};
shop.barber.set(Some(&barber));
println!("{:?}", barber.barber_state);
println!("{:?}", shop.shop_state);
shop.change_barber_state();
barber.change_shop_state();
println!("{:?}", barber.barber_state);
println!("{:?}", shop.shop_state);
}
Why do you think this should not compile?
Clearly both shop
and barber
life for the same extent, up to when main is finished. It doesn't matter the order of free in this case, since the compiler already know that none of them will be used anymore, so the above code is completely safe.
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