[英]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 和異步閉包之間的生命周期推斷有什么區別?
關閉版本失敗並出現以下錯誤:
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 ||
使用參數引用關閉。
生命周期概念是關於內存安全的,它歸結為保證指向內存插槽的引用指向有效值。
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
不再有效。
但是,life '2
是從哪里來'2
?
這是因為closure(&value)
返回一個實現Future
的實體,該實體將存在於運行時執行程序中,在本例中為 tokio 執行程序,直到計算結束。
'2
生命周期將考慮Future
這種有效性范圍。
出於'2
生命周期必要性'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
生命周期”,但這與理解此主題無關。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.