Hi everyone
I can't seem to explain the following lifetime-related behaviour in rust. Assume the following code (loosely inspired by rustc):
#![allow(dead_code, unused_imports)]
use std::cell::RefCell;
use std::marker::PhantomData;
#[derive(Default)]
pub struct GlobalCtxt<'gcx> {
dummy: PhantomData<&'gcx ()>, // avoid unused lifetime
// (A) The following fails:
maybe_fn: RefCell<Option<Box<Fn() + 'gcx>>>,
// (B) But the following two variants work:
// maybe_fn: RefCell<Option<Box<Fn()>>>,
// maybe_fn: Option<Box<Fn() + 'gcx>>,
}
fn main() {
let gcx = GlobalCtxt::default();
do_stuff(&gcx);
}
fn do_stuff<'a>(_: &'a GlobalCtxt<'a>) {}
The point is that I want to take a reference of GlobalCtxt<'a>
with lifetime 'a
, represented by the call to do_stuff
.
The problem:
Compiling the code as is (see the playground) produces the following error:
error[E0597]: `gcx` does not live long enough
--> src/main.rs:19:14
|
19 | do_stuff(&gcx);
| ^^^^ borrowed value does not live long enough
20 | }
| -
| |
| `gcx` dropped here while still borrowed
| borrow might be used here, when `gcx` is dropped and runs the destructor for type `GlobalCtxt<'_>`
This only happens if the RefCell
with + 'gcx
bound is present. For any of the other cases ((B)
), dropping the + 'gcx
bound or dropping the RefCell
, the code compiles just fine. I see this pattern in the rustc compiler as well, and there taking the reference &mut arenas
seems to work just fine:
// Here related to arenas:
// src/librustc/ty/context.rs
pub struct AllArenas<'tcx> {
pub global: WorkerLocal<GlobalArenas<'tcx>>,
pub interner: SyncDroplessArena,
global_ctxt: Option<GlobalCtxt<'tcx>>,
}
// src/librustc_driver/driver.rs
let mut arenas = AllArenas::new();
phase_3_run_analysis_passes(
// ...
&mut arenas,
// ...
);
pub fn phase_3_run_analysis_passes<'tcx, F, R>(
// ...
arenas: &'tcx mut AllArenas<'tcx>,
// ...
) -> Result<R, CompileIncomplete> { /* ... */ }
// And also again here:
// src/librustc/ty/context.rs
pub struct TyCtxt<'a, 'gcx: 'tcx, 'tcx: 'a> {
gcx: &'gcx GlobalCtxt<'gcx>,
interners: &'tcx CtxtInterners<'tcx>,
dummy: PhantomData<&'a ()>,
}
How can the use of RefCell
inside the struct propagate a lifetime requirement all the way up to the point where the reference is taken? Is this somehow related to Drop
?
Thanks!