Confusion over Closure Lifetimes

Hello there!
I ran into an error message I don't understand yet

to showcase it, I've created a minimal setup

struct Abc {
    value: i32,
    f: Box<dyn Fn(&i32)>,
}

fn toAbc(value: i32, f: impl Fn(&i32)) -> Abc {
    Abc {
        value,
        f: Box::new(f),
    }
}

Error Message:

error[E0310]: the parameter type `impl Fn(&i32)` may not live long enough
--> src/simple.rs:13:12
|
13 |         f: Box::new(f),
|            ^^^^^^^^^^^
|
note: ...so that the type `impl Fn(&i32)` will meet its required lifetime bounds
--> src/simple.rs:13:12
|
13 |         f: Box::new(f),
|            ^^^^^^^^^^^
help: consider adding an explicit lifetime bound  `'static` to `impl Fn(&i32)`...
|
10 | fn toAbc(value: i32, f: impl Fn(&i32) + 'static) -> Abc {
|                         ^^^^^^^^^^^^^^^^^^^^^^^

Here I am struggling to understand, why the parameter f wouldn't life long enough. Don't I have ownership?

Thank you for reading through this

You have ownership over a generic closure (i.e., what it captures), but it may itself be borrowing a local.

For instance, imagine someone calling toAbc with:

let s = String::from("Hello, World!");
let f = |&n: &i32| {
    println!("Closure that captures `&s = {:?}` was called with arg `{}`", &s, n);
};
// we give ownership of `f` to `toAbc()`, but:
//   - `f` does not own `s`,
//   - `f` is borrowing from a local,
//   - the type of `f` is not `'static`.
let abc = toAbc(42, f);
drop(s);
(abc.f)(&abc.value); // Use after free!

To be able to have a Box<dyn Fn(...) -> _>, you cannot be borrowing from a local, which is expressed by a : 'static bound on the generic type parameter, or a + 'static bound on the impl ... implicit generic type parameter:

fn toAbc(value: i32, f: impl 'static + Fn(&i32)) -> Abc {
    Abc {
        value,
        f: Box::new(f),
    }
}
// or
fn toAbc<F> (value: i32, f: F) -> Abc
where
    F : 'static, // captured environment does not borrow a local
    F : Fn(&'_ i32), // captured environment is "Callable" (by shared ref)
{
    Abc {
        value,
        f: Box::new(f),
    }
}
3 Likes

thank you for this concise and quick explanation!

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