异步fn和异步闭包之间的生存期推断有什么区别? [英] What's the difference of lifetime inference between async fn and async closure?
问题描述
看下面的代码:
#![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);
});
}
为什么 is_a(sample)
有效,但是下一行无法编译?异步fn和异步闭包之间的生存期推断有什么区别?
Why does is_a(sample)
works but the next line fails to be compiled? What's the difference of lifetime inference between async fn and async closure?
关闭版本失败,并出现以下错误:
The closure version fails with the following error:
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 ||
闭包的返回类型是编译器生成的匿名类型.
The return type of an async ||
closure is an anonymous type generated by the compiler.
这种类型实现了 Future
,并捕获了另外一个生存期与 async ||
闭包的范围有关.
Such type implements a Future
and it captures an additional
lifetime related to the scope of the async ||
closure.
fn main() {
let closure = async move |data: &i32| { --+ '2 start
println!("data: {}", data); |
}; |
|
is_a(closure); |
v
}
异步块返回带有签名的类型,例如:
The async block returns a type with signature like:
impl Future<Output = SomeType> + '2 + '...
其中'2
是闭包的生存期.
请注意,使用异步函数而不是闭包时,没有额外的生存期要求.
Note that there isn't this additional lifetime requirement when using an async function instead of a closure.
当您像这样呼叫 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
实现的类型,但需要任何生存期:
because the closure
argument is a type implemented for a specific lifetime '2
but any lifetime is required:
fn is_a(_: impl for<'a> A<'a>) {}
请注意,您注意到的错误确实隐藏了另一个源自捕获的'2
生命周期的生命周期违规.
Note that the error you notice indeed hides another lifetime violation that originate from the captured '2
lifetime.
针对:
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 ||
闭包内使用参数引用.
that let me conclude that it is not possible to use argument references inside an async ||
closure.
生命周期概念是关于内存安全的,归结为保证参考指向内存插槽指向有效值.
The lifetime concept is about memory safety and it boils down to garantee that a reference pointing to a memory slot is pointing to a valid value.
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
返回并且堆栈展开.
Consider the above example: value is a memory slot allocated on the stack and it will be valid until
my_function
returns and the stack unwind.
当 my_function
返回时,生存期'1
考虑到 value
的有效范围引用& value
无效.
The lifetime '1
take account of the scope of validity of value
, when my_function
returns
the reference &value
is not more valid.
但是,终生'2
从何而来?
这是因为 closure(& value)
返回一个实现了 Future
的实体,该实体将存在于运行时执行器中,在这种情况下,是东京执行器,直到计算结束.
It is because closure(&value)
returns an entity that implements a Future
that will live into the runtime executor,
in this case the tokio executor, until the computation will end.
'2
的生存期将考虑 Future
的有效性范围.
The '2
lifetime will take this scope of validity of the Future
into account.
为了使'2
生命周期成为必要,请考虑以下情形:
For making a reason of '2
lifetime necessity consider the following scenario:
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
生存期,但这与理解这个主题无关.
Note that in reality tokio::spawn
needs that &data
reference has 'static
lifetime,
but this is irrelevant to understand this theme.
这篇关于异步fn和异步闭包之间的生存期推断有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!