async fn 和 async closure 之间的生命周期推断有什么区别?
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)
可以编译下一行却编译失败? async fn 和 async closure 的生命周期推断有什么区别?
关闭版本失败并出现以下错误:
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 ||
闭包的 return 类型是编译器生成的匿名类型。
这种类型实现了一个 Future
并且它捕获了一个额外的
与 async ||
闭包范围相关的生命周期。
fn main() {
let closure = async move |data: &i32| { --+ '2 start
println!("data: {}", data); |
}; |
|
is_a(closure); |
v
}
异步块return是一种签名类型,如:
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
return 秒,堆栈展开。
生命周期'1
考虑value
的有效期,当my_function
returns
引用 &value
无效。
但生命从何而来'2
?
这是因为 closure(&value)
return 是一个实现 Future
的实体,它将存在于运行时执行器中,
在本例中是 tokio 执行器,直到计算结束。
'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
生命周期,
但这与理解这个主题无关。
看这段代码:
#![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)
可以编译下一行却编译失败? async fn 和 async closure 的生命周期推断有什么区别?
关闭版本失败并出现以下错误:
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 ||
闭包的 return 类型是编译器生成的匿名类型。
这种类型实现了一个 Future
并且它捕获了一个额外的
与 async ||
闭包范围相关的生命周期。
fn main() {
let closure = async move |data: &i32| { --+ '2 start
println!("data: {}", data); |
}; |
|
is_a(closure); |
v
}
异步块return是一种签名类型,如:
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
return 秒,堆栈展开。
生命周期'1
考虑value
的有效期,当my_function
returns
引用 &value
无效。
但生命从何而来'2
?
这是因为 closure(&value)
return 是一个实现 Future
的实体,它将存在于运行时执行器中,
在本例中是 tokio 执行器,直到计算结束。
'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
生命周期,
但这与理解这个主题无关。