想在闭包参数里传可变借用,需要如何解决生命周期问题?
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();
}
1
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(); } ``` |
2
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(); } |