Lifetimes and reference chains / scopes

This can't be done as you imagined. You can tell this by the anti-pattern &'a mut Type<'a>. A mutable reference to an object with the same lifetime parameter is not usable in the vast majority of cases, because it needs to be mutably (and thus, exclusively) borrowed for its entire lifetime.

In order to fix this, one usually introuces a fresh lifetime parameter, say, 'b: 'a and writes &'a mut Type<'b> instead. This, however, is not possible in the recursive case, because you would need &'a mut Writer<'b, 'c> (in order to distinguish both lifetime parameters from that of the reference), but then 'c would also need to be a lifetime parameter, so now you would have &'a mut Writer<'b, 'c, 'd>, but then 'd would also need to be a new lifetime parameter, etc.

Since immutable borrows don't have the uniqueness requirement, you could "fix" this by switching to dynamic borrow checking using RefCell instead (or Mutex / RwLock if you need thread safety in the future). Then you can usefully materialize a type such as &'a RefCell<Writer<'a>>, like this:

pub struct Writer<'a> {
    parent: Option<&'a RefCell<Writer<'a>>>,
}

impl<'a> Writer<'a> {
    pub fn push(this: &'a RefCell<Self>) -> RefCell<Self> {
        RefCell::new(Writer {
            parent: Some(this),
        })
    }
    
    pub fn add(&mut self) {}
}

Of course, this negates all advantages of having a static borrow checker, because your code will now panic if you accidentally keep a borrow alive longer than you intended to. So perhaps a design around owned values would be more appropriate? I.e. don't store references or RefCells; store Option<Box<Writer>> instead. Or just maintain an explicit stack of writers in a Vec<Writer>, and optionally, store the index of its parent writer in any given child.

2 Likes