FnMut lifetime problem

Can't seem to get this simple example to work - can anybody tell me what I'm doing wrong?
I've tried playing around with the possible lifetime combinations but nothing seems to work...

In my head, I'm trying to capture a local variable into a callback, which is then updated in the update function later on... there are 2 relevant lifetimes involved to me here:

  • 1st is the lifetime of the mut ref to data that outlives everything (reference inside closure)
  • 2nd is the lifetime of the result of calling the closure, this only needs to be as long as the update() lifetime, which is shorter than the 1st.
fn update<'a: 'b, 'b, F>(mut func: F)
where
    F: (FnMut() -> &'b mut bool) + 'a,
{
    *func() = true;
}

fn main() {
    let mut data = false;
    update(|| &mut data);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
  --> src/main.rs:10:15
   |
10 |     update(|| &mut data);
   |               ^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'_` as defined on the body at 10:12...
  --> src/main.rs:10:12
   |
10 |     update(|| &mut data);
   |            ^^^^^^^^^^^^
note: ...so that closure can access `data`
  --> src/main.rs:10:15
   |
10 |     update(|| &mut data);
   |               ^^^^^^^^^
note: but, the lifetime must be valid for the expression at 10:5...
  --> src/main.rs:10:5
   |
10 |     update(|| &mut data);
   |     ^^^^^^
note: ...so that a type/lifetime parameter is in scope here
  --> src/main.rs:10:5
   |
10 |     update(|| &mut data);
   |     ^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0495`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

I've explored this a bit and I think the cause is twofold:

  1. you're borrowing the data from inside the closure, but returning a reference to data stored outside of the closure (so the returned reference can't hold the closure borrowed)
  2. you're using FnMut, so the closure must be callable multiple times

To find this, I wrote this even smaller usage:


fn call_update1<'a>(v: &'a mut bool) {
    let closure = || v;
    update(closure);
}
fn call_update2<'a>(v: &'a mut bool) {
    let closure = || &mut *v;
    update(closure);
}

The first errors saying the closure is only FnOnce, not FnMut - so it can't be passed in.

The second errors with a message similar to yours - I think referencing the fact that the borrow is recreated inside the closure, but the lifetimes you've specified force the borrow to be valid independent of the closure's existence.

We can kind of see how this would fail if allowed, too, by the fact that the following update function compiles:

fn update<'a: 'b, 'b, F>(mut func: F)
where
    F: (FnMut() -> &'b mut bool),
{
    let borrow1 = func();
    let borrow2 = func();
    *borrow1 = true;
    *borrow2 = false;
}

If the closure satisfied your bounds, then it would allow multiple simultaneous mutable borrows to the same data, which would be bad.

Note: there isn't any syntax in rust today which would allow you to have a closure which returns a reference to itself, so we can't explore fixing it that way. You can do that with a custom trait, like trait GetMut<T> { fn get_mut<'a>(&'a self) -> &'a T; }, but it would require implementing it for custom structs rather than using closures.

The only other way I would fix this, then, is by making the function take an FnOnce rather than FnMut. This function makes everything compile:

fn update<'b, F>(mut func: F)
where
    F: (FnOnce() -> &'b mut bool),
{
    *func() = true;
}

And to double verify, with the new function signature, the double-mutable-borrow is also now disallowed:

fn update<'b, F>(mut func: F)
where
    F: (FnOnce() -> &'b mut bool),
{
    let borrow1 = func();
    let borrow2 = func(); // ~ERROR: use of moved value: `func`
    *borrow1 = true;
    *borrow2 = false;
}
1 Like

Thanks for helping - I think you're right as well. I made up an example of showing how your custom trait solution works: Link

I'm using FnMut as it does need to be called multiple times, and it needs to mutate state (through a mut ref). Probably should have included that in the example sorry.

But yes... I guess there's nothing to tie the lifetime of the returned reference to the closure when doing it that way. A custom trait solves this by specifying this relationship explicitly.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.