bianweiall
V2EX  ›  Rust

Rust 闭包可变借用参数生命周期如何处理?

By bianweiall at 2022 年 11 月 9 日 · 2842 次点击

想在闭包参数里传可变借用,需要如何解决生命周期问题?

rustc 1.65.0

296 |       do_demo(|demo| async move {
    |  ______________-----_^
    | |              |   |
    | |              |   return type of closure `impl Future<Output = String>` contains a lifetime `'2`
    | |              has type `&'1 mut Demo`
297 | |         println!("demo: {:?}", demo);
298 | |         demo.print_mut();
299 | |         format!("test demo, id: {}", demo.id)
300 | |     })
    | |_____^ returning this value requires that `'1` must outlive `'2`
use std::future::Future;

#[derive(Debug)]
struct Demo {
    id: i32,
    name: String,
}

impl Demo {
    fn print(self) {
        println!("id:{}, name:{:?}", self.id, self.name);
    }
    fn print_mut(&mut self) {
        println!("id:{}, name:{:?}", self.id, self.name);
    }
}

async fn do_demo<F, U>(mut f: F) -> Result<(), String>
where
    F: for<'a> FnMut(&'a Demo) -> U,
    U: Future<Output = String>,
{
    let demo = Demo {
        id: 1,
        name: "test".to_string(),
    };

    let ret = f(&demo).await;

    demo.print();

    println!("f() ret: {:?}", ret);

    Ok(())
}

#[tokio::test]
async fn demo() {
    do_demo(|demo| async move {
        println!("demo: {:?}", demo);
        demo.print_mut();
        format!("test demo, id: {}", demo.id)
    })
    .await
    .unwrap();
}

3 条回复  •  2022-11-18 23:03:54 +08:00
ihciah
   1
ihciah  
   2022 年 11 月 9 日
因为生成的 Future 捕获了 &Demo ,所以要求 &Demo 生命周期长于 Future 。
但 async 闭包返回值的类型写不出来,所以不好约束。一个办法是手动定义带生命周期标记的结构并实现 Future ,但明显实际用起来不太好使。
不知道有没有其他 hack 的办法,我想到一个办法是自定义一个类似 Fn/FnMut 这种的 Trait ,然后在关联类型上标生命周期,这样 fn 定义上就可以把 &Demo 的生命周期和 Future 关联起来了。

```
#![feature(type_alias_impl_trait)]

use std::future::Future;

#[derive(Debug)]
struct Demo {
id: i32,
name: String,
}

impl Demo {
fn print(self) {
println!("id:{}, name:{:?}", self.id, self.name);
}
fn print_ref(&self) {
println!("id:{}, name:{:?}", self.id, self.name);
}
}

trait MyFn {
type Future<'a>: Future<Output = String>;
fn call<'a>(&self, param: &'a Demo) -> Self::Future<'a>;
}

struct DemoFn;
impl MyFn for DemoFn {
type Future<'a> = impl Future<Output = String> + 'a;

fn call<'a>(&self, param: &'a Demo) -> Self::Future<'a> {
async move {
println!("demo: {:?}", param);
param.print_ref();
format!("test demo, id: {}", param.id)
}
}
}

async fn do_demo<F>(f: F) -> Result<(), String>
where
F: MyFn,
{
let demo = Demo {
id: 1,
name: "test".to_string(),
};

let ret = f.call(&demo).await;

// demo.print();

println!("f() ret: {:?}", ret);

Ok(())
}

async fn demo() {
do_demo(DemoFn)
.await
.unwrap();
}
```
liuxu
   2
liuxu  
   2022 年 11 月 10 日
异步或者多线程,所有权还是应该交出去,借用把事情搞复杂了,四不像了

use std::future::Future;

#[derive(Debug, Clone)]
struct Demo {
id: i32,
name: String,
}

impl Demo {
#[allow(dead_code)]
fn print(&self) {
println!("id:{}, name:{:?}", self.id, self.name);
}
fn print_mut(&mut self) {
println!("id:{}, name:{:?}", self.id, self.name);
}
}

async fn do_demo<F, U>(f: F) -> Result<(), String>
where
F: for<'a> FnOnce(Demo) -> U,
U: Future<Output = String>,
{
let demo = Demo {
id: 1,
name: "test".to_string(),
};

let ret = f(demo.clone()).await;

demo.print();

println!("f() ret: {:?}", ret);

Ok(())
}

#[tokio::main]
async fn main() {
do_demo(|mut demo| async move {
println!("demo: {:?}", demo);
demo.print_mut();
format!("test demo, id: {}", demo.id)
})
.await
.unwrap();
}
chinawrj
   3
chinawrj  
   2022 年 11 月 18 日 via Android
@liuxu 正解。。
© 2026 V2EX · 37ms · 3.9.8.5