Borrowed data escapes outside of closure, but I don't see how it would

Hi all,

I am working on a class of the following form:

impl<E> Tree<E> {
  pub fn map<F:Clone, G:FnMut(&E) -> F>(&self, mut f: G) -> Tree<F> {
  // ...
  }
}

impl<'a, E> Interner<'a, E> where E: Eq + Hash + Clone + Display {
  pub fn add_object_byref(&'a mut self, obj: &E) -> usize {
  // ...
  }

  pub fn intern_tree(&'a mut self, tree: &Tree<E>) -> Tree<usize> {
        tree.map(|label: &E| { self.add_object_byref(label) })
    }
}

The compiler complains that "borrowed data escapes outside of closure" in the tree.map call, namely self. Could someone help me understand what the problem is here? I have read everything I could find about this error message, but still can't figure out how self is going to escape from the closure here. I can't even figure out how to build a minimal example (my apologies for the incomplete code snippet) - my attempts to recreate the error in simpler contexts have all failed.

What I'm trying to do is to build a map method that will apply an FnMut closure to each node of a tree and then return a tree made of the results of the closure calls. The closure needs to be FnMut so it can mutate captured values, as in the example. The method add_object_byref doesn't do anything with its obj argument except put a clone of it into an internal data structure.

Having a type Foo<'a> with methods taking &'a mut self is probably not going to do what you want. The borrow of self has to last as long as whatever lifetime the type contains which is (almost) always useless.

2 Likes

This isn't exactly that problem — 'a doesn't appear in the type. I agree that the 'a may be a problem here, though.

@alexanderkoller Try removing the 'a lifetime, and letting each &mut self have its own distinct lifetime (which need not be written explicitly).

It appears in the type of the impl on Interner. Is that handled differently somehow?

Removing all the lifetime annotations gets rid of this problem. Can you help me understand why?

I added the lifetimes because I tried to migrate a Vec field to the Bumpalo implementation, which seems to require an explicit lifetime annotation. Here's the struct for Interner:

pub struct Interner<E: Eq+Hash> {
    int2object: Vec<E>, 
    // or bumpalo::collections::vec::Vec<'a, E>

    object2int: HashMap<E, usize>,
}

Do you have an idea where I can get the 'a lifetime for the Bumpalo Vec, if not from a lifetime on the struct itself? (I suppose technically this is supposed to be the lifetime of a Bump object that gets passed to the constructor of Interner - but I don't think I can know that at compile time.)

You can have a lifetime on Interner, but you shouldn't need to use that same lifetime on &mut self.

I think the error happens for the following reason: in intern_tree, self has the type &'a mut Interner<'a, E>. When you pass it into the closure, it's not actually passed as-is, it is reborrowed with the shorter lifetime corresponding to the closure. Couln't really be otherwise in the general case, since that closure may be returned from the function and live its separate borrowed life. But add_object_byref also takes &'a mut Interner<'a, E>, which means that any shorter lifetime won't be accepted by it. You are effectively trying to widen the lifetime of reborrowed self back to 'a, which is interpreted by the help system as "escaping from the closure".

3 Likes

That makes sense, thank you.

So how do I work around this? I guess the correct thing to do is to make sure add_object_byref only takes &mut self without further lifetime annotation, and then somehow figure out a way to do whatever I need to do within add_object_byref without knowing that self has lifetime 'a? I think that's also what @semicoleon is suggesting.

Some experimentation shows that I was almost correct. The reborrow due to a closure's capture doesn't matter here, we can move the capture into closure and the error is the same. However, you need to be able to reborrow self within the closure in order to pass it into add_object_byref, and that is where the lifetimes conflict. The reborrow must happen since you cannot just move &'a mut Self into that function, because the closure may be called several times.

If you change the signature of map to require FnOnce closure, the captured &'a mut Self parameter can be safely moved into add_object_byref, and the code compiles successfully. Of course, using FnOnce in a map function doesn't actually make sense, but at least we have pinpointed the issue.

Yes, you shouldn't take &'a mut self parameters, that doesn't make any sense. This means that the Interner<'a, E> is essentially borrowed for the entirety of its lifetime, which isn't even possible to do in safe code. Covariance with respect to lifetimes can help here, but still that signature doesn't make much sense,

Normally, you shouldn't hold a mutable borrow longer than required to make modification, which is provided by &mut self receiver type. Why do you think you need more specific constraints on that borrow?

Just to be clear that example compiles, but if you actually try to use the interner more than once you get errors

Playground

fn main() {
    let inner = "inner".to_string();
    let tree = Tree("Tree".to_string());

    let mut intern = Interner(&inner);

    intern.intern_tree(&tree);

    intern.intern_tree(&tree);
}
error[E0499]: cannot borrow `intern` as mutable more than once at a time
  --> src/main.rs:37:5
   |
35 |     intern.intern_tree(&tree);
   |     ------------------------- first mutable borrow occurs here
36 |
37 |     intern.intern_tree(&tree);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
   |     |
   |     second mutable borrow occurs here
   |     first borrow later used here

Sure, because the signature says that the borrow lives as long as the Interner, and that means that There Can Be Only One. That's more of an exercise in solving compiler errors than a useful signature.

1 Like

In case it was lost in the discussion -- does this change solve your OP or does it result in a new problem?

 impl<'a, E> Interner<'a, E> where E: Eq + Hash + Clone + Display {
-  pub fn add_object_byref(&'a mut self, obj: &E) -> usize {
+  pub fn add_object_byref(&mut self, obj: &E) -> usize {
   // ...
   }

-  pub fn intern_tree(&'a mut self, tree: &Tree<E>) -> Tree<usize> {
+  pub fn intern_tree(&mut self, tree: &Tree<E>) -> Tree<usize> {
         tree.map(|label: &E| { self.add_object_byref(label) })
     }
 }

The problem with &'a mut Thing<'a>, like &'a mut self in your OP, is that

  • you're requiring the inner and outer lifetime to be the same
  • the inner lifetime is invariant (cannot be approximated with a shorter lifetime)
  • so the Thing is exclusively borrowed for the rest of its valid lifetime

You can safely create such a borrow, but then you can never use the Thing<'a> again, except through that borrow. You can't even call its destructor (so if it has one, you'll get an error).

1 Like

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.