Is it possible to accept async closures without `Pin<Box<dyn...>>` or nightly?

Hey folks, is there anyway to get this to compile without using nightly or Pin<Box<dyn Future..>>?

struct Transaction<'x, 's> {
    a: &'x mut String,
    b: &'x mut String,
    c: &'s mut Vec<i32>,
}

struct State {
    a: String,
    b: String,
    c: Vec<i32>,
}

trait Statement<O, E> {
    async fn run(self, transaction: Transaction) -> Result<O, E>;
}
impl<S, O, E, F> Statement<O, E> for S
where
    S: Send + FnOnce(Transaction<'_, '_>) -> F,
    F: Future<Output = Result<O, E>>,
    O: Send + 'static,
    E: Send + 'static,
{
    async fn run(self, transaction: Transaction<'_, '_>) -> Result<O, E> {
        self(transaction).await
    }
}

impl State {
    async fn transaction<F, O, E>(&mut self, f: F) -> Result<O, E>
    where
        F: Statement<O, E>,
        O: Send + 'static,
        E: Send + 'static,
    {
        let mut a = self.a.clone();
        let mut b = self.b.clone();

        let transaction = Transaction {
            a: &mut a,
            b: &mut b,
            c: &mut self.c,
        };
        let out = f.run(transaction).await?;
        self.a = a;
        self.b = b;
        Ok(out)
    }
}

struct Other;

struct Container {
    state: State,
    other: Other,
}

#[tokio::main]
async fn main() {
    let mut container = Container {
        state: State {
            a: "Hello".to_string(),
            b: "World".to_string(),
            c: vec![1, 2, 3],
        },
        other: Other,
    };

    let r: Result<(), ()> = container
        .state
        .transaction(|transaction| async move { Ok(()) })
        .await;
}

the error message is:

error: implementation of `FnOnce` is not general enough
  --> src/main.rs:70:29
   |
70 |       let r: Result<(), ()> = container
   |  _____________________________^
71 | |         .state
72 | |         .transaction(|transaction| async move { Ok(()) })
   | |_________________________________________________________^ implementation of `FnOnce` is not general enough
   |
   = note: closure with signature `fn(Transaction<'2, '_>) -> {async block@src/main.rs:72:36: 72:57}` must implement `FnOnce<(Transaction<'1, '_>,)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnOnce<(Transaction<'2, '_>,)>`, for some specific lifetime `'2`

error: implementation of `FnOnce` is not general enough
  --> src/main.rs:70:29
   |
70 |       let r: Result<(), ()> = container
   |  _____________________________^
71 | |         .state
72 | |         .transaction(|transaction| async move { Ok(()) })
   | |_________________________________________________________^ implementation of `FnOnce` is not general enough
   |
   = note: closure with signature `fn(Transaction<'_, '2>) -> {async block@src/main.rs:72:36: 72:57}` must implement `FnOnce<(Transaction<'_, '1>,)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnOnce<(Transaction<'_, '2>,)>`, for some specific lifetime `'2`

error: implementation of `FnOnce` is not general enough
  --> src/main.rs:70:29
   |
70 |       let r: Result<(), ()> = container
   |  _____________________________^
71 | |         .state
72 | |         .transaction(|transaction| async move { Ok(()) })
73 | |         .await;
   | |______________^ implementation of `FnOnce` is not general enough
   |
   = note: closure with signature `fn(Transaction<'2, '_>) -> {async block@src/main.rs:72:36: 72:57}` must implement `FnOnce<(Transaction<'1, '_>,)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnOnce<(Transaction<'2, '_>,)>`, for some specific lifetime `'2`

error: implementation of `FnOnce` is not general enough
  --> src/main.rs:70:29
   |
70 |       let r: Result<(), ()> = container
   |  _____________________________^
71 | |         .state
72 | |         .transaction(|transaction| async move { Ok(()) })
73 | |         .await;
   | |______________^ implementation of `FnOnce` is not general enough
   |
   = note: closure with signature `fn(Transaction<'_, '2>) -> {async block@src/main.rs:72:36: 72:57}` must implement `FnOnce<(Transaction<'_, '1>,)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnOnce<(Transaction<'_, '2>,)>`, for some specific lifetime `'2`

error: implementation of `FnOnce` is not general enough
  --> src/main.rs:73:10
   |
73 |         .await;
   |          ^^^^^ implementation of `FnOnce` is not general enough
   |
   = note: closure with signature `fn(Transaction<'2, '_>) -> {async block@src/main.rs:72:36: 72:57}` must implement `FnOnce<(Transaction<'1, '_>,)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnOnce<(Transaction<'2, '_>,)>`, for some specific lifetime `'2`

error: implementation of `FnOnce` is not general enough
  --> src/main.rs:73:10
   |
73 |         .await;
   |          ^^^^^ implementation of `FnOnce` is not general enough
   |
   = note: closure with signature `fn(Transaction<'_, '2>) -> {async block@src/main.rs:72:36: 72:57}` must implement `FnOnce<(Transaction<'_, '1>,)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnOnce<(Transaction<'_, '2>,)>`, for some specific lifetime `'2`

warning: `rust` (bin "rust") generated 3 warnings

Thank you so much for your time in advance :slight_smile:

You can lift your transaction closure to an item-level async fn, but if you want to use a closure, it's sadly impossible on current stable to write a higher-ranked closure (one that accepts any lifetime '1 and not some specific lifetime '2).

Bummer. I'll just stick with Pin<Box<dyn Future<Output = ...>>> then.

Thank you!

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.