Lifetime from generic in impl trait return

Is there any way to make something like this work?

fn bar<S, W: FnOnce(&mut S)>(build: W) {}

fn foo<E>() -> impl FnOnce(E) {
    |_| {}
}

fn somefunction() {
    bar::<bool, _>(foo::<&mut bool>())
}

The compiler error is sort of unclear:

error[E0308]: mismatched types
 --> src/lib.rs:8:5
  |
3 | fn foo<E>() -> impl FnOnce(E) {
  |                --------------
  |                |
  |                the expected opaque type
  |                the found opaque type
...
8 |     bar::<bool, _>(foo::<&mut bool>())
  |     ^^^^^^^^^^^^^^ lifetime mismatch
  |
  = note: expected associated type `<impl FnOnce(&mut bool) as FnOnce<(&mut bool,)>>::Output`
             found associated type `<impl FnOnce(&mut bool) as FnOnce<(&mut bool,)>>::Output`
note: the lifetime requirement is introduced here
 --> src/lib.rs:1:14
  |
1 | fn bar<S, W: FnOnce(&mut S)>(build: W) {}
  |              ^^^^^^^^^^^^^^

I've tried this, but that doesn't change anything:

fn foo<E>() -> impl FnOnce(E)
    where for<'a> E: 'a
{
    |_| {}
}

I suspect this might also have to do with variance. Am I correct in thinking that all impl trait lifetimes are invariant?

Playground

The problem is that bar expects something that implements for<'a> FnOnce<&'a mut S>, which means that it should implement the following infinite list of traits:

  • FnOnce<&'lifetime1 mut S>
  • FnOnce<&'lifetime2 mut S>
  • FnOnce<&'lifetime3 mut S>
  • ...
  • FnOnce<&'static mut S>

where the list goes through every possible lifetime. However, foo returns something that implements the single trait FnOnce<E> for some specific type E. The type E could be &'lifetime1 mut S, or it could be &'lifetime2 mut S, but no matter which type you choose, it is only going to implement one of the traits from the infinite list, but it must implement all of the traits on the infinite list.

If you defined foo like this, then it could work:

fn foo<E>() -> impl FnOnce(&mut E) {
    |_| {}
}
6 Likes

The issue is that in my real use case I need to be able to have the type sometimes be a tuple of two mutable references (which could in turn contain more mutable references). Now interestingly this works:

fn bar<S, W: FnOnce(&mut S)>(build: W) {}

fn foo<E>() -> impl FnOnce(&mut E) {
    |_| {}
}

fn somefunction() {
    bar::<(&mut bool, &mut bool), _>(foo())
}

but this doesn't:

fn foo<E>() -> impl FnOnce(&mut E) {
    |_| {}
}

fn somefunction() -> impl FnOnce(&mut (&mut bool, &mut bool)) {
    foo()
}

I think that's because with bar::<(&mut bool, &mut bool), _> it's infering some specific lifetime which makes it work. If I do this, it also doesn't work:

fn bar<W: FnOnce(&mut (&mut bool, &mut bool))>(build: W) {}

fn foo<E>() -> impl FnOnce(&mut E) {
    |_| {}
} 

fn somefunction() {
    bar(foo())
}

I think what I'd like to express is something like:

fn foo<E<'a>>() -> impl for<'a> FnOnce(E<'a>)

Where E would take a lifetime that I could then supply in the returned closure, but I'm guessing that such a thing isn't possible currently.

Correct (or two specific lifetimes) -- it has to, so that S in bar<S, W> can be resolved to a single type. Where as foo cannot meet the bound of

impl FnOnce(&mut (&mut bool, &mut bool))

aka

impl for<'x, 'y, 'z> FnOnce(&'x mut (&'y mut bool, &'z mut bool))

as there are infinte (&'y mut book, &'z mut bool) while foo's E is just one type. It can only satisfy a couple of specific lifetimes.


Since the return of foo can handle any outer lifetime, do you really care about handling any inner lifetimes? I.e. does this work for your use-case?

fn foo<E>() -> impl FnOnce(&mut E) {
    |_| {}
}

fn somefunction<'a, 'b>() -> impl FnOnce(&mut (&'a mut bool, &'b mut bool)) {
    foo()
}

It works for simple cases, but it's a lot less flexible due to the invariance of &mut. Once the two inner lifetimes are locked in, they can't change.


Correct, Rust has no type-constructing generics like that so far.

1 Like

My use case looks kind of like this:

use core::marker::PhantomData;

trait Widget<E> {
    type State;

    fn do_widget_things(&mut self, state: &mut Self::State, env: &mut E);
}

fn stateful<E, S, V, W: for<'a> Widget<(&'a mut S, &'a mut E), State = V>, B: Fn(&mut S, &mut E) -> W>(build: B) -> impl Widget<E> {
    StatefulWidget {
        s: PhantomData,
        build,
    }
} 

struct StatefulWidget<S, B> {
    s: PhantomData<fn() -> S>,
    build: B,
}

impl<E, S, V, W: for<'a> Widget<(&'a mut S, &'a mut E), State = V>, B: Fn(&mut S, &mut E) -> W> Widget<E> for StatefulWidget<S, B> {
    type State = (S, V);

    fn do_widget_things(&mut self, state: &mut Self::State, env: &mut E) {
        let mut widget = (self.build)(&mut state.0, env);

        widget.do_widget_things(&mut state.1, &mut (&mut state.0, env));
    }
}

fn button<E>(on_click: fn(&mut E)) -> impl Widget<E> {
    Button(on_click)
}

struct Button<F>(F);

impl<E, F: Fn(&mut E)> Widget<E> for Button<F> {
    type State = ();

    fn do_widget_things(&mut self, state: &mut (), env: &mut E) {
        self.0(env);
    }
}

fn mywidget<E>() -> impl Widget<E> {
    stateful::<E, bool, _, _, _>(|state: &mut bool, env: &mut E| {
        button::<(&mut bool, &mut E)>(|e| {
            *e.0 = true;
        })
    })
}

It's not actually using closures, but impl Widgets, that usually contain closures, but the problem is the same. I can make some variations of this work by using concrete types instead of impl trait returns, but for the system to work in a composable manner, it needs to work with impl trait.

I think something similar to your suggestion might work, but I'm not sure.

I've tried this and this, but so far haven't figured out how to make it work. (The code annotations are a bit messy because I've tried a bunch of different things.) I would much prefer the first one, but I'm not sure if that's possible. The second one I think should be possible, but I haven't figured out how yet. Maybe I also just have to change the general approach.

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.