Search code examples
rustrust-tokio

why can't i pass an async function as a field when it takes mutable references?


I was working on a structure that had asynchronous functions as fields, but it didn't work when it took mutable references.

this works:

#![allow(dead_code)]

pub struct TestStruct;

struct TestStruct2<F>
where
    F: std::future::Future<Output = ()>,
{
    foo: fn(TestStruct) -> F,
}

async fn foo(_x: TestStruct) {}

#[tokio::main]
async fn main() {
    let _s = TestStruct2 { foo };
}

this does not work:

#![allow(dead_code)]

pub struct TestStruct;

struct TestStruct2<F>
where
    F: std::future::Future<Output = ()>,
{
    foo: fn(&mut TestStruct) -> F,
}

async fn foo(_x: &mut TestStruct) {}

#[tokio::main]
async fn main() {
    let _s = TestStruct2 { foo }; //error here
}

error:

mismatched types
expected fn pointer `for<'r> fn(&'r mut TestStruct) -> _`     
found fn item `for<'r> fn(&'r mut TestStruct) -> impl std::future::Future<Output = ()> {foo}`

could someone explain to me why this happens and what can I do about it?


Solution

  • The issue isn't the mut, it's the reference and its lifetime. According to the Asynchronous Programming in Rust book, the return values of async functions have their lifetimes bounded by their arguments' lifetimes. Your definition of TestStruct2::foo doesn't accept this. To fix that, you can add this lifetime explicitly to F:

    struct TestStruct2<'a, F>
    where
        F: std::future::Future<Output = ()> + 'a,
    {
        foo: fn(&'a mut TestStruct) -> F,
    }