Closure with struct field being a function

struct A {
    f: Box<dyn Fn(i32) -> bool>
}

fn get(x: i32) -> bool {
    x > 10
}

let a = A { f: Box::new(get) };

impl A {
    fn to_get(&self) -> Box<dyn Fn(i32) -> bool> {
        Box::new(
            move |x: i32| (self.f)(x)
        )
    }
}

Error: lifetime may not live long enough
   ╭─[command_28:1:1]
   │
 2 │         fn to_get(&self) -> Box<dyn Fn(i32) -> bool> {
   ·                   ┬                                │ 
   ·                   ╰────────────────────────────────── let's call the lifetime of this reference `'1`
   ·                                                    │ 
   ·                                                    ╰─ help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound: ` + '_`
 3 │ ╭─▶         Box::new(
   ⋮ ⋮   
 5 │ ├─▶         )
   · │               
   · ╰─────────────── returning this value requires that `'1` must outlive `'static`

The error says that the instance a must outlive static, which means a must static too?
So, when I need the method to_get to return Box<dyn Fn(i32) -> bool>, what is the right way to do it?

Trait objects (like dyn Fn(i32) -> bool) always have an associated lifetime; you can often elide the lifetime, but it has contextual defaults which don't always line up with what you want. In a function header, a trait object inside a Box has a default lifetime of 'static. So an expanded signature of your function is

    fn to_get(&self) -> Box<dyn Fn(i32) -> bool + 'static> {

However, what you're returning captures &self, and that's not 'static. If you're going to capture &self like this, you have to return something that's valid for no longer than the &self reference.

Therefore the minimal fix is this:

-    fn to_get(&self) -> Box<dyn Fn(i32) -> bool> {
+    fn to_get(&self) -> Box<dyn Fn(i32) -> bool + '_> {
         Box::new(move |x: i32| (self.f)(x))
     }

If you want struct A to be able to handle lifetime-limited closures, it will need a lifetime too (but this may not be a good idea depending on your larger goals).

2 Likes

Oh, and one other note: if you just want to return a borrow of self.f without altering it (by wrapping it in a new closure), you could instead do

    fn to_get(&self) -> &dyn Fn(i32) -> bool {
        &self.f
    }

(The return could also be annotated as -> &dyn Fn(i32) -> bool + '_.)

2 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.