gpt4 book ai didi

rust - 异步fn和异步闭包之间的生存期推断有什么区别?

转载 作者:行者123 更新时间:2023-12-03 11:34:18 24 4
gpt4 key购买 nike

看下面的代码:

#![feature(async_closure)]

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

trait A<'a> {
fn call(&'a self, data: &'a i32) -> Pin<Box<dyn 'a + Future<Output=()>>>;
}

impl <'a, F, Fut> A<'a> for F
where Fut: 'a + Future<Output=()>,
F: Fn(&'a i32) -> Fut
{
fn call(&'a self, data: &'a i32) -> Pin<Box<dyn 'a + Future<Output=()>>> {
Box::pin(self(data))
}
}

async fn sample(_data: &i32) {

}

fn is_a(_: impl for<'a> A<'a>) {

}

fn main() {
is_a(sample);
is_a(async move |data: &i32| {
println!("data: {}", data);
});
}

Playground

为什么 is_a(sample)可以工作,但是下一行无法编译?异步fn和异步闭包之间的生存期推断有什么区别?

封闭版本因以下错误而失败:

error: implementation of `A` is not general enough
--> src/main.rs:29:5
|
6 | / trait A<'a> {
7 | | fn call(&'a self, data: &'a i32) -> Pin<Box<dyn 'a + Future<Output=()>>>;
8 | | }
| |_- trait `A` defined here
...
29 | is_a(async move |data: &i32| {
| ^^^^ implementation of `A` is not general enough
|
= note: `A<'1>` would have to be implemented for the type `[closure@src/main.rs:29:10: 31:6]`, for any lifetime `'1`...
= note: ...but `A<'_>` is actually implemented for the type `[closure@src/main.rs:29:10: 31:6]`, for some specific lifetime `'2`

最佳答案

async ||闭包的返回类型是由编译器生成的匿名类型。

这种类型实现了Future并捕获了一个额外的
生存期与async ||闭包的范围有关。

fn main() {

let closure = async move |data: &i32| { --+ '2 start
println!("data: {}", data); |
}; |
|
is_a(closure); |
v
}

异步块返回带有签名的类型,如:
impl Future<Output = SomeType> + '2 + '...

其中 '2是闭包的生存期。

请注意,使用异步函数而不是闭包时,没有额外的生存期要求。

当您像这样调用 is_a时:
let closure = async move |data: &i32| {
println!("data: {}", data);
};

is_a(closure);

你得到:
error: implementation of `A` is not general enough
...
= note: `A<'1>` would have to be implemented for the type `[closure@src/main.rs:43:19: 45:6]`, for any lifetime `'1`...
= note: ...but `A<'_>` is actually implemented for the type `[closure@src/main.rs:43:19: 45:6]`, for some specific lifetime `'2`

因为 closure参数是为特定生存期 '2实现的类型,但是任何生存期都是必需的:
fn is_a(_: impl for<'a> A<'a>) {}

请注意,您注意到的错误确实隐藏了另一个源自捕获的 '2生命周期的生命周期违规。

为了:
let closure = async move |data: &i32| {
println!("data: {}", data);
};

编译器报告:
error: lifetime may not live long enough
--> src/main.rs:43:19
|
43 | let closure = async move |data: &i32| {
| ^^^^^^^^^^^^^^^^^^-^^^-
| | | |
| | | return type of closure is impl std::future::Future
| | let's call the lifetime of this reference `'1`
| returning this value requires that `'1` must outlive `'2`

这使我得出结论,不可能在 async ||闭包内使用参数引用。

为什么需要终生'2?

终生概念与内存安全有关,可以归结为保证引用
指向内存插槽指向有效值。
fn my_function() {

let value = 1 --+ '1 start
|
let closure = async move |data: &i32| { | --+ '2 start
println!("data: {}", data); | |
}; | |
| |
tokio::spawn(closure(&value)) | |
-+ '1 end |
} v continue until
the future complete

考虑上面的示例:value是在堆栈上分配的内存插槽,它将一直有效,直到 my_function返回,堆栈展开。

'1返回时,生存期 value考虑了 my_function的有效范围
引用 &value无效。

但是,终生 '2从何而来?

这是因为 closure(&value)返回一个实现 Future的实体,该实体将存在于运行时执行程序中,
在这种情况下,是东京执行器,直到计算结束。
'2生存期将考虑 Future的有效性范围。

为了使 '2生存期成为必要,请考虑以下情形:
fn run_asyn_closure() {
let data: i32 = 1;

let closure = async move |data: &i32| {
println!("starting task with data {}", data);

// yield the computation for 3 seconds, awaiting for completion of long_running_task
long_running_task().await;

// data points to a memory slot on the stack that meantime is rewritten
// because run_asyn_closure returned 3 seconds ago
println!("using again data: {}", data); // BANG!! data is not more valid
};

tokio::spawn(closure(&data));
}

请注意,实际上 tokio::spawn需要 &data引用具有 'static生存期,
但这与理解此主题无关​​。

关于rust - 异步fn和异步闭包之间的生存期推断有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60751127/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com