Constructor of closures returning closures: what lifetimes to use?

I want to implement a generic, boxed mutable closure with the following characteristics:

  • The closure will be called indefinitely often.
  • The closure takes a generic M parameter.
  • The closure can designate a new, boxed, mutable closure to be used in subsequent invocations. Otherwise, the same closure will be used.

Additionally, I want to provide a constructor to ease the construction of the closure (i.e., avoid the need of manually boxing the closure).

Here is formulation 1:

enum NextV1<M> {
    Same,
    Closure(Box<dyn FnMut(M) -> NextV1<M> + 'static>),
}

fn box_it_v1<M>(cl: impl FnMut(M) -> NextV1<M> + 'static) -> Box<dyn FnMut(M) -> NextV1<M> + 'static> {
    Box::new(cl)
}

Bonus: Does box_it_v1 desugar to the following?

fn box_it_v1<M>(cl: impl FnMut(M) -> Next<M> + 'static) -> Box<dyn FnMut(M) -> Next<M> + 'static> {
    Box::new(cl)
}

Here is formulation 2:

enum NextV2<'a, M> {
    Same,
    Closure(Box<dyn FnMut(M) -> NextV2<'a, M> + 'a>),
}

fn box_it_v2<'a, M>(cl: impl FnMut(M) -> NextV2<'a, M> + 'a) -> Box<dyn FnMut(M) -> NextV2<'a, M> + 'a> {
    Box::new(cl)
}

What are the differences between these two formulations? Do they entail practical differences?

Can you provide some snippets where an approach works and the other doesn't?

Example: Rust Playground

1 Like

Those are the same code except one uses NextV1 and the other uses Next (which you never defined).

The first one only supports closures that don't capture any non-'static lifetimes, such as a local reference. The second one does support those, at the cost of having an invariant lifetime parameter infecting everything.

The first one can satisfy a 'static bound if M: 'static. The second one requires 'a: 'static and M: 'static.

1 Like

To answer the question in the subject, using a lifetime is more general but also more verbose and visually distracting. If you know you don't need to support borrow-capturing closures, go with the version without the lifetime parameter.