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

2022-11-09 17:11:42 +08:00
 bianweiall

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

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();
}

1273 次点击
所在节点    Rust
3 条回复
ihciah
2022-11-09 19:01:35 +08:00
因为生成的 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
2022-11-10 13:55:34 +08:00
异步或者多线程,所有权还是应该交出去,借用把事情搞复杂了,四不像了

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
2022-11-18 23:03:54 +08:00
@liuxu 正解。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/893906

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX