Rust Lifetime Puzzle: RefCell and Lifetime Issues

use std::cell::RefCell;  

struct StructWithLifetime<'a> {  
    _a: &'a i32,  
}  

impl Drop for StructWithLifetime<'_> {  
    fn drop(&mut self) {}  
}  

struct Manager<'a> {  
    a: RefCell<StructWithLifetime<'a>>,  
}  

struct Weird<'a> {  
    manager: &'a Manager<'a>,  
}  

fn main() {  
    let manager = Manager {  
        a: RefCell::new(StructWithLifetime { _a: &0 }),  
    };  

    let weird = Weird { manager: &manager };  
}

Removing the drop , replacing RefCell with Box , or introducing two lifetimes for Weird can all resolve the issue. However, I don't understand the underlying reason. Can someone explain?

UnsafeCell<T> (which underlies RefCell) is invariant in T. Due to invariance, the compiler can't shrink 'a, resulting in a type &'a UnsafeCell<T<'a>> that is borrowed forever. This is similar to the &'a mut T<'a> antipattern:

You can't call methods on it, you can't take another reference to it, you can't move it, you can't print it, you can't use it at all. You can't even call a non-trivial destructor on it; if you have a non-trivial destructor, your code won't compile in the presence of &'a mut Thing<'a>.

7 Likes