< 返回版块

bianweiall 发表于 2022-11-09 17:20

Tags:闭包,生命周期,future

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

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

评论区

写评论
wangbyby 2022-11-10 23:12

这样子是否符合你的需求呢?

trait DemoGAT {
    type Out<'a>;

    fn demo<'b, F>(&'b mut self, f: F) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
    where
        F: for<'a> FnMut(&'a mut Self) -> Self::Out<'a> + Send +'b;
}

impl DemoGAT for Demo {
    type Out<'a> = Pin<Box<dyn Future<Output = &'a str> + Send + 'a>>;
    fn demo<'b, F>(&'b mut self, mut f: F) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
    where
        F: for<'a> FnMut(&'a mut Self) -> Self::Out<'a> + Send + 'b,
    {
        Box::pin(async move{
            let a = f(self).await;
            println!("{:?}", a);
            ()
        })
    }
}

fn demo_life<'a>(demo: &'a mut Demo) -> Pin<Box<dyn Future<Output = &'a str> + Send + 'a>>{
    demo.set_name();
    Box::pin(async {
        demo.name.as_str()
    })
    
}

#[tokio::main]
async fn main() {
    let mut demo = Demo {
        id: 1,
        name: "a".to_string(),
    };
    println!("demo: {:?}", demo);
    demo.demo(demo_life).await;
    println!("demo: {:?}", demo);
}

--
👇
bianweiall: 感谢几位朋友的帮助,只使用GAT, 当我传入的闭包也需要返回Future时,在下面的示例中要如何去实现?

--
👇
wangbyby: 我理解,可能你需要GAT?

use std::future::Future;
use std::pin::Pin;

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

impl Demo {
    fn print(&self) {
        println!("id:{}, name:{:?}", self.id, self.name);
    }
    fn set_name(&mut self) {
        self.name = format!("test demo, id: {}", self.id);
    }
}

trait DemoGAT {
    type Out<'a>;

    fn demo<F>(&mut self, f: F) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
    where
        F: for<'a> FnMut(&'a mut Self) -> Self::Out<'a>;
}

impl DemoGAT for Demo {
    type Out<'a> = &'a str;
    fn demo<F>(&mut self, mut f: F) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
    where
        F: for<'a> FnMut(&'a mut Self) -> Self::Out<'a>
    {
        f(self);
        Box::pin(async move { () })
    }
}

#[tokio::main]
async fn main() {
    let mut demo = Demo {
        id: 1,
        name: "a".to_string(),
    };
    println!("demo: {:?}", demo);
    demo.demo(|demo: &mut Demo| -> &str {
        
        demo.set_name();
        &demo.name
    })
    .await;
    println!("demo: {:?}", demo);
}

作者 bianweiall 2022-11-10 15:31

感谢几位朋友的帮助,只使用GAT, 当我传入的闭包也需要返回Future时,在下面的示例中要如何去实现?

--
👇
wangbyby: 我理解,可能你需要GAT?

use std::future::Future;
use std::pin::Pin;

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

impl Demo {
    fn print(&self) {
        println!("id:{}, name:{:?}", self.id, self.name);
    }
    fn set_name(&mut self) {
        self.name = format!("test demo, id: {}", self.id);
    }
}

trait DemoGAT {
    type Out<'a>;

    fn demo<F>(&mut self, f: F) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
    where
        F: for<'a> FnMut(&'a mut Self) -> Self::Out<'a>;
}

impl DemoGAT for Demo {
    type Out<'a> = &'a str;
    fn demo<F>(&mut self, mut f: F) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
    where
        F: for<'a> FnMut(&'a mut Self) -> Self::Out<'a>
    {
        f(self);
        Box::pin(async move { () })
    }
}

#[tokio::main]
async fn main() {
    let mut demo = Demo {
        id: 1,
        name: "a".to_string(),
    };
    println!("demo: {:?}", demo);
    demo.demo(|demo: &mut Demo| -> &str {
        
        demo.set_name();
        &demo.name
    })
    .await;
    println!("demo: {:?}", demo);
}

wangbyby 2022-11-09 23:00

个人觉得#![feature(type_alias_impl_trait)]更加优雅一些

--
👇
Paradox: 想起来 GAT 已经 stable 了,那 GAT 应该也能做?

--
👇
Paradox: 这里报错的大概意思是无法推断返回的闭包的生命周期与参数生命周期的关系,所以只要在函数签名上标注就好了。

但就我所知,stable rust似乎目前还做不到这一点,需要用到其他 feature 来做到这一点,这里我用了 #![feature(type_alias_impl_trait)]

playground


不过如上面说的,async runtime 要求生命周期为 'static,所以看起来并没有什么用(

wangbyby 2022-11-09 22:58

我理解,可能你需要GAT?

use std::future::Future;
use std::pin::Pin;

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

impl Demo {
    fn print(&self) {
        println!("id:{}, name:{:?}", self.id, self.name);
    }
    fn set_name(&mut self) {
        self.name = format!("test demo, id: {}", self.id);
    }
}

trait DemoGAT {
    type Out<'a>;

    fn demo<F>(&mut self, f: F) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
    where
        F: for<'a> FnMut(&'a mut Self) -> Self::Out<'a>;
}

impl DemoGAT for Demo {
    type Out<'a> = &'a str;
    fn demo<F>(&mut self, mut f: F) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>
    where
        F: for<'a> FnMut(&'a mut Self) -> Self::Out<'a>
    {
        f(self);
        Box::pin(async move { () })
    }
}

#[tokio::main]
async fn main() {
    let mut demo = Demo {
        id: 1,
        name: "a".to_string(),
    };
    println!("demo: {:?}", demo);
    demo.demo(|demo: &mut Demo| -> &str {
        
        demo.set_name();
        &demo.name
    })
    .await;
    println!("demo: {:?}", demo);
}

fakeshadow 2022-11-09 22:32

HRTB问题,一些简单代码看似可以但并不能在async中使用捕获变量。如果可以接受捕获函数的限制是不需要nightly的,例如: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=11d3892fc874d229f8ec30e0b940cf44

Paradox 2022-11-09 19:00

想起来 GAT 已经 stable 了,那 GAT 应该也能做?

--
👇
Paradox: 这里报错的大概意思是无法推断返回的闭包的生命周期与参数生命周期的关系,所以只要在函数签名上标注就好了。

但就我所知,stable rust似乎目前还做不到这一点,需要用到其他 feature 来做到这一点,这里我用了 #![feature(type_alias_impl_trait)]

playground


不过如上面说的,async runtime 要求生命周期为 'static,所以看起来并没有什么用(

Paradox 2022-11-09 18:34

这里报错的大概意思是无法推断返回的闭包的生命周期与参数生命周期的关系,所以只要在函数签名上标注就好了。

但就我所知,stable rust似乎目前还做不到这一点,需要用到其他 feature 来做到这一点,这里我用了 #![feature(type_alias_impl_trait)]

playground


不过如上面说的,async runtime 要求生命周期为 'static,所以看起来并没有什么用(

豆沙饼 is Louys 2022-11-09 18:15

When you spawn a task on the async runtime, its type's lifetime must be 'static. This means that the spawned task must not contain any references to data owned outside the task.

It is a common misconception that 'static always means "lives forever", but this is not the case. Just because a value is 'static does not mean that you have a memory leak. You can read more in Common Rust Lifetime Misconceptions.

https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md#2-if-t-static-then-t-must-be-valid-for-the-entire-program

豆沙饼 is Louys 2022-11-09 18:11

async 不是强制要求'static 吗? 能传借用?

1 共 9 条评论, 1 页