Borrow of self-referential field requires that it outlives the lifetime of the self-referencing reference

I don't understand why the borrow must last for 'a. Are there any ways around this without using unsafe, Rc/Weak, or external crates? In my real use case, self_ref is a BTreeSet and data is an array, so using indices as opposed to references doesn't really work, and storing keys is just annoying.

fn main() {
    let mut foo = Foo::default();
    
    // If Foo::bar takes &'a mut self, this mutable borrow
    // lasts as long as long as the lifetime of foo...
    foo.bar();
    
    // ... making this borrow (mutable or not), impossible.
    foo.baz();
    
    // Meanwhile, if Foo::bar takes &mut self, the borrow
    // is shorter lived.
}

#[derive(Debug, Default)]
struct Foo<'a> {
    self_ref: Vec<&'a i32>,
    data: i32,
}

impl<'a> Foo<'a> {
    fn bar(&mut self) {
        let data = &self.data;
        
        // This borrow to self.self_ref requires that the
        // borrow outlives 'a.
        // Annotating the borrow as &'a mut self resolves
        // the error, but disallows any further borrows
        // after this one.
        self.self_ref.push(data);
    }
    
    fn baz(&self) {
        println!("{}", self.self_ref.len());
    }
}

Please avoid self-referential structs.

2 Likes

The first thing to try is separating self_ref and data so they are not in the same struct. For example, it might work to pass them as separate parameters. I realize this is probably inconvenient, but it is worth examining.

1 Like

There's no sound ways around the "borrow forever" lifetime using references, because if Foo<'a> does not remain borrowed, you could obtain an exclusive and shared reference to data at the same time, which is UB. In non-reduced use cases, it usually also means you can throw out the referenced data, opening the door to dangling references, UAFs, and the like.

For example I think you meant self_ref is an array and data is the BTreeSet (as otherwise storing indices would work fine); if you can get a &mut Foo<'_> you can clear the BTreeSet.

You could set things up so that you still have a &Foo after becoming self-referencial to make the reduced example work,[1] but it's still more restrictive than just using two different structs,[2] so not worth it IMO even if it happens to work.

TL;DR: References (&, &mut) are the wrong tool for the job.


  1. return &Self from bar ↩︎

  2. you still can't use foo ever again, including moves or destructors ↩︎

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.