rust 来自外部函数的future内部可变引用

wlsrxk51  于 2022-11-24  发布在  其他
关注(0)|答案(2)|浏览(147)

可以使用以下代码片段就地修改函数参数:

let mut foo = 1;
let mut fun: Box<dyn FnMut(&mut i32) -> _> = Box::new(|f| {
    *f += 1;
});
fun(&mut foo);
assert_eq!(foo, 2);

然而,我有这样一个例子,函数fun需要返回一个future,一旦future被等待,它就修改参数。基本上我有下面的场景:

let mut foo = 1;
assert_eq!(foo, 1);
let mut fun: Box<dyn FnMut(&mut i32) -> _> = Box::new(|f| {
    async move {
        *f += 1;
    }
});
fun(&mut foo).await;
assert_eq!(foo, 2);

rust playground
但这会产生编译错误:

error: lifetime may not live long enough
 --> src/main.rs:7:9
  |
6 |       let mut fun: Box<dyn FnMut(&mut i32) -> _> = Box::new(|f| {
  |                                                              -- return type of closure `impl Future<Output = ()>` contains a lifetime `'2`
  |                                                              |
  |                                                              has type `&'1 mut i32`
7 | /         async move {
8 | |             *f += 1;
9 | |         }
  | |_________^ returning this value requires that `'1` must outlive `'2`

error: could not compile `playground` due to previous error

我不知道如何在上面的代码片段中注解生存期。我已经尝试过Box<dyn FnMut(&'static mut i32) -> _>,但这表明foo的生存期不够长。
有没有办法让它工作?

ezykj2lf

ezykj2lf1#

您不能简单地更改(可能)来自另一个线程的值,而不使用一些同步原语将其打包。
相反,您可以将它与ArcMutex打包,并像处理多线程程序一样处理它:

use std::sync::{Arc, Mutex};

#[tokio::main]
async fn main() {
    let foo = Arc::new(Mutex::new(1));
    assert_eq!(*foo.lock().unwrap(), 1);
    let mut fun: Box<dyn FnMut(Arc<Mutex<i32>>) -> _> = Box::new(|f| {
        async move {
            let mut counter = f.lock().unwrap();
            *counter += 1;
        }
    });
    fun(Arc::clone(&foo)).await;
    assert_eq!(*foo.lock().unwrap(), 2);
}
2izufjch

2izufjch2#

我尝试将函数fun移动到泛型函数定义中,而不是对其进行装箱,这似乎是可行的:

fn fun<'a>(f: &'a mut i32) -> impl futures::Future<Output = ()> + 'a {
    async move {
        *f += 1;
    }
}

#[tokio::main]
async fn main() {
    let mut foo = 1;
    assert_eq!(foo, 1);
    fun(&mut foo).await;
    assert_eq!(foo, 2);
}

这似乎是可行的,因为我可以在这个设置中注解生存期。这个解决方案要求我立即等待它,因为&mut i32一次只能存在于一个地方。也就是说,下面的方法行不通:

#[tokio::main]
async fn main() {
    let mut foo = 1;
    assert_eq!(foo, 1);
    let fut = fun(&mut foo);
    assert_eq!(foo, 1);
    fut.await;
    assert_eq!(foo, 2);
}

相关问题