1

I'm trying to pass a function as one of the arguments to another function and I'm running into a one type is more general than the other error.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e4074efd53fdf9e43d8f209405745dc0

use std::future::Future;

async fn constraint<A, Fut, L>(args: A, lambda: L)
where
    A: 'static + Send,
    Fut: Future<Output = ()> + 'static,
    L: Fn(&A) -> Fut + 'static,
{
    lambda(&args).await;
}

fn main() {
    constraint("hello".to_string(), lambda);
}

async fn lambda(_: &String) -> () {}

Here is the error message:

error[E0308]: mismatched types
  --> src/main.rs:13:5
   |
13 |     constraint("hello".to_string(), lambda);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected trait `for<'a> <for<'a> fn(&'a String) -> impl Future<Output = ()> {lambda} as FnOnce<(&'a String,)>>`
              found trait `for<'a> <for<'a> fn(&'a String) -> impl Future<Output = ()> {lambda} as FnOnce<(&'a String,)>>`
note: the lifetime requirement is introduced here
  --> src/main.rs:7:18
   |
7  |     L: Fn(&A) -> Fut + 'static,
   |                  ^^^

For more information about this error, try `rustc --explain E0308`.

I don’t want to change the lambda function because I want to be able to call it multiple times in a while loop with a second argument from an input stream, but I can change constraint function.

So far I tried adding a lifetime to the constraint function but I'm not sure if that is on the right path since there should already be a static lifetime on A:

async fn constraint<'a, A, Fut, L>(args: A, lambda: L)
where
    A: 'static + Send,
    Fut: Future<Output = ()> + 'static,
    L: Fn(&'a A) -> Fut + 'static,
{
    lambda(&args).await;
}

Corresponding error:

   |
3  | async fn constraint<'a, A, Fut, L>(args: A, lambda: L)
   |                     -- lifetime `'a` defined here
...
9  |     lambda(&args).await;
   |     -------^^^^^-
   |     |      |
   |     |      borrowed value does not live long enough
   |     argument requires that `args` is borrowed for `'a`
10 | }
   | - `args` dropped here while still borrowed

Also tried to move the argument but got the same error:

use std::future::Future;

async fn constraint<A, Fut, L>(args: A, lambda: L)
where
    A: 'static + Send,
    Fut: Future<Output = ()> + 'static,
    L: Fn(&A) -> Fut + 'static,
{
    async move {
        lambda(&args).await;
    };

}

fn main() {
    constraint("hello".to_string(), lambda);
}

async fn lambda(_: &String) -> () {}

1 Answers1

0

It's because the impl Future return type of your lambda implicitly captures the lifetime of it's argument, but your constraint requires it to be 'static (Fut: Future<Output = ()> + 'static)

Your lambda desugars to something like this:

fn lambda<'a>(_: &'a String) -> impl Future<Output = ()> + 'a {
    async {}
}

where it's output has a lifetime bound to it's input argument.

While constraint expected the signature to look something like this:

fn lambda<'a>(_: &'a String) -> impl Future<Output = ()> + 'static {
    async {}
}

You can just use that last definition (the explicit lifetime 'a is not required).

I'm not sure if and how you can convey that meaning using async

cafce25
  • 15,907
  • 4
  • 25
  • 31