Cannot make Fn closure with RwLock

Hi, I would love to pass a Fn closure to a function which runs repeatedly in tokio::spawn. The closure will read a shared value, which could be modified in other threads, usgng tokio::sync::RwLock.

When I wrote such a program, the compiler said the following error message. In this message, rw is a RwLock.

closure is `FnOnce` because it moves the variable `rw` out of its environment

I wrote the following code as a minimum sample (playground code is here).

use std::sync::Arc;
use tokio::sync::RwLock;
use futures::Future;

async fn test_spawn<R>(f: impl Fn() -> R)
where
    R: Future<Output = ()>,
{
    loop {

       tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
        f();
    }
}

async fn test() {
    let rw = Arc::new(RwLock::new(42));

    let f = move || async move {
        let v = rw.read().await;
        println!("{}", *v);
    };

    let t = tokio::spawn(test_spawn(f));
    let (r,) = tokio::join!(t);
    r.unwrap();
}

I have tried the same thing without closure, and this works fine.

use std::sync::Arc;
use tokio::sync::RwLock;
use futures::Future;

async fn test_spawn(rw: Arc<RwLock<i32>>)
{
    loop {
        tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;

        let v = rw.read().await;
        println!("{}", *v);
    }
}

async fn test() {
    let rw = Arc::new(RwLock::new(42));

    let t = tokio::spawn(test_spawn(rw));
    let (r,) = tokio::join!(t);
    r.unwrap();
}

How should I change the code to compile successfully? I would appreciate if you have any advice.

Thanks!

This is because the first time you call the closure, the Arc<RwLock> is moved into the async block that the closure returns. Since it has now given up ownership of the Arc<RwLock>, it would be invalid to call it again.

To fix this, clone the arc before moving it into the async block.

let f = move || {
    let rw = rw.clone();
    async move {
        let v = rw.read().await;
        println!("{}", *v);
    }
};
4 Likes

This works perfect, thanks a lot!