Captured variable cannot escape FnMut closure body

code that causes error:

pub fn main() {
let mut a=vec![11,34,12];

let mut cl=||{a.push(15);a.last().unwrap()};

}

But when I change it to it, the error disappears:


pub fn main() {
let mut a=vec![11,34,12];

let mut cl=||{a.last().unwrap()};

}


I have fully read and understand the meaning of the error, but why does it occur only in the first case?

First version modifies vector which means it has unique mutable reference to it. Unique references are, well… unique. And thus you can not return shared reference to the element because it would clash with unique mutable reference to the whole vector.

Second version doesn't change anything, it's only receives shared read-only reference… and these can be easily copied around.

2 Likes

This works thanks to reborrowing (I think):

1 Like

FnMut closures can only return a single type, including lifetimes, so the situation is similar to this:

// Compiles
struct JustLast<'a>(&'a Vec<i32>);
impl<'a> JustLast<'a> {
    fn call(&self) -> Option<&'a i32> {
        self.0.last()
    }
}

// Fails
struct PushThenLast<'a>(&'a mut Vec<i32>);
impl<'a> PushThenLast<'a> {
    fn call_mut(&mut self) -> Option<&'a i32> {
        self.0.push(15);
        self.0.last()
    }
}

You can get a &'long _ out from a &'short self.0 by copying, and thus always return an Option<&'a i32> in the case of JustLast. But you can only get a &'short mut _ out from &'short mut self in the case of PushThenLast.

This signature would work:

    fn call_mut(&mut self) -> Option<&i32> {

but the FnMut trait doesn't allow that; it only allows return values independent of the &mut self borrow.

1 Like

refs live only until their last use, so a unique mutable reference to the vector drop here, isn't it?

What do you mean by “here”? What happens if you call that closure twice?

I don't know if this is what you meant either, but you can nudge the compiler to create a FnOnce closure instead.

    fn nudge_inferrence<'a, F: FnOnce() -> &'a i32>(f: F) -> F { f }
    let cl = nudge_inferrence(|| {
        a.push(15);
        a.last().unwrap()
    });

This is now analogous to

struct PushThenLast<'a>(&'a mut Vec<i32>);
impl<'a> PushThenLast<'a> {
    fn call_once(self) -> &'a i32 {
        self.0.push(15);
        self.0.last().unwrap()
    }
}
1 Like

Yes, it makes sense