V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
bianweiall
V2EX  ›  Rust

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

  •  
  •   bianweiall · 2022-11-09 17:11:42 +08:00 · 1425 次点击
    这是一个创建于 730 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    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-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
        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();
    }
    chinawrj
        3
    chinawrj  
       2022-11-18 23:03:54 +08:00 via Android
    @liuxu 正解。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2848 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 13:15 · PVG 21:15 · LAX 05:15 · JFK 08:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.