Return boxed function

I've got a struct, one of whose fields is a function whose type is not known at compile time. In trying to get this to work, I've done:

struct Foo<T> {
    init: Box<Fn() -> T>,
}

However, I'm having trouble getting the lifetimes to work. I have the following implementation that involves two different constructors which each initialize init differently:

trait Initializable {
    fn init() -> Self;
}

struct Foo<T> {
    init: Box<Fn() -> T>,
}

impl<T> Foo<T> {
    fn new<F: Fn() -> T>(init: F) -> Foo<T> {
        Foo { init: Box::new(init) }
    }
}

impl<T: Initializable> Foo<T> {
    fn new_initializable() -> Foo<T> {
        Foo { init: Box::new(T::init) }
    }
}

However, I get a compiler error relating to lifetimes (I won't paste it here because it's long and verbose, but you can see it by trying to compile the source here).

Does anyone know a way around this? I've gone down the rabbit hole of trying to annotate different types or functions with lifetimes, and haven't managed to get it to work. Thanks!

Boxes implicitly have 'static lifetime. You'll either have to restrict the functions you accept for boxing like this, or carry a lifetime everywhere like this.

1 Like

Ah, OK gotcha. Is there any way to get the best of both worlds by using something other than Box?

What is "the best of both worlds" to you? The flexibility of a struct accepting any lifetime without specifying lifetimes? I don't think there's any way to do this today, but there have been a number of discussions on allowing lifetime elisions in more scenarios.

FWIW, I would probably just use Default instead of your Initializable, e.g.:

impl<'a, T: Default + 'a> Default for Foo<'a, T> {
    fn default() -> Self {
        Foo { init: Box::new(T::default) }
    }
}

Oh good call on Default - I wasn't aware that existed.

Honestly now that I think about it, I don't think my "best of both worlds" question made sense other than with more lifetime elisions as you suggested. Basically I was thinking of some way of the users of this Foo type not have to type any lifetime parameters but still have the compiler prove that the init argument to new lives long enough.