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