Converting FnOnce to AsyncFnOnce

⚓ Rust    📅 2025-08-21    👤 surdeus    👁️ 4      

surdeus

I have the following async fn, see Playground

async fn transactional_save<F>(
    db: DatabaseConnection,
    callback: F,
) -> Result<User, TransactionError<DbErr>>
where
    F: FnOnce(&User, &DatabaseTransaction) + Send + 'static,
{
    db.transaction::<_, User, DbErr>(|txn| {
        Box::pin(async move {
            let user = User;

            callback(&user, txn);

            Ok(user)
        })
    })
    .await
}

and I am trying to convert

F: FnOnce(&User, &DatabaseTransaction) + Send + 'static,

To

F: ASyncFnOnce(&User, &DatabaseTransaction) + Send + 'static,

So that callback(&user, txn); becomes callback(&user, txn).await?;

My Attempt

This is what I have tried, see Playground

async fn transactional_save<F>(
    db: DatabaseConnection,
    callback: F,
) -> Result<User, TransactionError<DbErr>>
where
    F: AsyncFnOnce(&User, &DatabaseTransaction) + Send + 'static,
{
    db.transaction::<_, User, DbErr>(|txn| {
        Box::pin(async move {
            let user = User;

            callback(&user, txn).await;

            Ok(user)
        })
    })
    .await
}

error: future cannot be sent between threads safely
  --> src/lib.rs:45:9
   |
45 | /         Box::pin(async move {
46 | |             let user = User;
47 | |
48 | |             callback(&user, txn).await;
49 | |
50 | |             Ok(user)
51 | |         })
   | |__________^ future created by async block is not `Send`
   |
   = help: within `{async block@src/lib.rs:45:18: 45:28}`, the trait `Send` is not implemented for `<F as AsyncFnOnce<(&User, &db::DatabaseTransaction)>>::CallOnceFuture`
note: future is not `Send` as it awaits another future which is not `Send`
  --> src/lib.rs:48:13
   |
48 |             callback(&user, txn).await;
   |             ^^^^^^^^^^^^^^^^^^^^ await occurs here on type `<F as AsyncFnOnce<(&User, &db::DatabaseTransaction)>>::CallOnceFuture`, which is not `Send`
   = note: required for the cast from `Pin<Box<{async block@src/lib.rs:45:18: 45:28}>>` to `Pin<Box<dyn Future<Output = Result<User, db::DbErr>> + Send>>`

What does the compiler try to tell me? I cannot comprehend the meaning. I thought I have marked F to be Send ?
If I change the signature to the following then it compiles.

 //  F: AsyncFnOnce(&User, &DatabaseTransaction) + Send + 'static,
    F: FnOnce(&User, &DatabaseTransaction) -> Fut + Send + 'static,
    Fut: Future<Output = ()> + Send

2 posts - 2 participants

Read full topic

🏷️ Rust_feed