Help with sort of higher-kinded lifetimes

I'm trying to create a generic fn create_fn that wraps a user fn my_func1 by building the arguments. I'm running into some trouble appeasing the borrow-checker however. Any help would be appreciated.

trait EatsLifetimeInner<'port, 'a: 'port> {
    type T;
}
trait EatsLifetime: for<'port, 'a> EatsLifetimeInner<'port, 'a> {}
impl<T: ?Sized> EatsLifetime for T where Self: for<'port, 'a> EatsLifetimeInner<'port, 'a> {}
type Wrap<'port, 'a, T> = <T as EatsLifetimeInner<'port, 'a>>::T;

#[derive(Debug)]
struct Port(u32);
type OutputPorts<'port> = [&'port mut Port];
type ReactionFn = dyn FnMut(&'_ mut OutputPorts<'_>) + Sync + Send;

fn create_fn<F, O>(f: F) -> Box<ReactionFn>
where
    F: Fn(Wrap<'_, '_, O>) + 'static + Send + Sync,
    O: EatsLifetime,
    for<'port, 'a> Wrap<'port, 'a, O>: From<&'a mut OutputPorts<'port>>,
{
    Box::new(move |outputs| {
        let x: <O as EatsLifetimeInner>::T = outputs.into();
        f(x)
    })
}

mod test {
    use super::*;

    #[derive(Debug)]
    struct Outputs1<'port> {
        my_port: &'port mut Port,
    }
    impl<'port, 'a: 'port> EatsLifetimeInner<'port, 'a> for Outputs1<'_> {
        type T = Outputs1<'port>;
    }
    impl<'port, 'a: 'port> From<&'a mut OutputPorts<'port>> for Outputs1<'port> {
        fn from(outputs: &'a mut OutputPorts<'port>) -> Self {
            let [port0]: &mut [&mut Port; 1] = outputs.try_into().unwrap();
            Self { my_port: port0 }
        }
    }

    #[test]

    fn test2() {
        let mut p0 = Port(0);
        let mut p1 = Port(0);
        let mut s = [&mut p0, &mut p1];
        let o = s.as_mut_slice();
        let c = Outputs1::from(o);
        dbg!(c);

        fn my_func1(o: Outputs1) {
            o.my_port.0 += 1;
            dbg!(o);
        }

        let my_f = create_fn::<_, Outputs1<'_>>(my_func1);

        my_f(o);
    }
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/lib.rs:20:54
   |
20 |         let x: <O as EatsLifetimeInner>::T = outputs.into();
   |                                                      ^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined here...
  --> src/lib.rs:19:14
   |
19 |       Box::new(move |outputs| {
   |  ______________^
20 | |         let x: <O as EatsLifetimeInner>::T = outputs.into();
21 | |         f(x)
22 | |     })
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/lib.rs:20:46
   |
20 |         let x: <O as EatsLifetimeInner>::T = outputs.into();
   |                                              ^^^^^^^
note: but, the lifetime must be valid for the anonymous lifetime #2 defined here...
  --> src/lib.rs:19:14
   |
19 |       Box::new(move |outputs| {
   |  ______________^
20 | |         let x: <O as EatsLifetimeInner>::T = outputs.into();
21 | |         f(x)
22 | |     })
   | |_____^
note: ...so that the types are compatible
  --> src/lib.rs:20:54
   |
20 |         let x: <O as EatsLifetimeInner>::T = outputs.into();
   |                                                      ^^^^
   = note: expected `From<&mut [&mut Port]>`
              found `for<'port, 'a> From<&'a mut [&'port mut Port]>`

For more information about this error, try `rustc --explain E0495`.
error: could not compile `playground` due to previous error
warning: build failed, waiting for other jobs to finish...
error: implementation of `EatsLifetimeInner` is not general enough
  --> src/lib.rs:57:20
   |
57 |         let my_f = create_fn::<_, Outputs1<'_>>(my_func1);
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `EatsLifetimeInner` is not general enough
   |
   = note: `Outputs1<'_>` must implement `EatsLifetimeInner<'0, '1>`, for any two lifetimes `'0` and `'1`...
   = note: ...but `Outputs1<'_>` actually implements `EatsLifetimeInner<'_, '2>`, for some specific lifetime `'2`

error: implementation of `From` is not general enough
  --> src/lib.rs:57:20
   |
57 |         let my_f = create_fn::<_, Outputs1<'_>>(my_func1);
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `From` is not general enough
   |
   = note: `Outputs1<'0>` must implement `From<&'1 mut [&'0 mut Port]>`, for any two lifetimes `'0` and `'1`...
   = note: ...but `Outputs1<'_>` actually implements `From<&'2 mut [&mut Port]>`, for some specific lifetime `'2`

error: build failed

The issue is in the definition of EatsLifetimeInner, which is declared as trait EatsLifetimeInner<'port, 'a: 'port>. This means that a type can implement EatsLifetimeInner<'port, 'a> only if 'a: 'port. Meanwhile, when we use the type &'a mut OutputPorts<'port>, we introduce the implied bound 'port: 'a.

Therefore, in the where clauses of create_fn, the second clause asserts that for all 'a, 'port where 'a: 'port, we have O: EatsLifetimeInner<'port, 'a>. Similarly, the third clause asserts that for all 'a, 'port where 'a == 'port, we have Wrap<'port, 'a, O>: From<&'a mut OutputPorts<'port>>.

Now, for create_fn to return a Box<ReactionFn>, we need the closure to implement FnMut(&'_ mut OutputPorts<'_>) + Sync + Send. This means that for all 'a, 'port where 'port: 'a, we have [closure]: FnMut(&'a mut OutputPorts<'port>).

However, the closure body does not produce a valid closure for all 'a, 'port where 'port: 'a. The outputs.into() expression depends on Wrap<'port, 'a, O>: From<&'a mut OutputPorts<'port>>. Meanwhile, Wrap<'port, 'a, O> is a valid type only if 'a: 'port (due to the bound on EatsLifetimeInner). This means that if 'a is any shorter than 'port, there is no such Into trait that the call can resolve to.

The compiler message is somewhat confusing due to the anonymous lifetimes and implied bounds. AFAICT, "the lifetime cannot outlive the anonymous lifetime #1" is referring to the 'port: 'a bound implied by &'a mut OutputPorts<'port>. "The lifetime must be valid for the anonymous lifetime #2" is referring to the fact that Wrap<'port, 'a> isn't always From<&'a mut [&'port mut Port]>, since it is only defined when 'a: 'port. This results in the error, since into() won't always be available given the lifetimes.

Ultimately, the solution is pretty simple. If you want to use EatsLifetime in this way, then remove the 'a: 'port bound:

-trait EatsLifetimeInner<'port, 'a: 'port> {
+trait EatsLifetimeInner<'port, 'a> {
     type T;
 }

This resolves the error in the definition of create_fn. However, the Outputs1 impl no longer works, since dereferencing an &'a mut &'port mut Port produces an &'a mut Port, not an &'port mut Port as it expects. I'm not sure exactly what you're trying to do with it, but I'm not sure if it's possible.

1 Like

Hi @LegionMammal978, thank you very much for your help here.

Essentially what I'm trying to do is write a function that wraps a user function:

type X = &mut [&mut T];
fn wrap<O: From(X), F: Fn(O)>(f: F) -> Box<dyn Fn(X)>
{
  Box::new(move |x| f(x.into())
}

I've updated the playground here: Rust Playground

Why don't create_wrapper1 or create_wrapper2 work for your use case? Note that the compiler cannot be tricked through lifetime hiding, it only makes the error messages more confusing.

create_wrapper2 works if you drop the wrapper after using it. create_wrapper5 is like create_wrapper4 but with the 'a on the outside lifetime. But it's not really any more useful than create_wrapper2.

Incidentally, &'a mut Thing<'a> is an anti-pattern, as the Thing is borrowed for its entire lifetime. That's probably why create_wrapper1 doesn't work.

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.