Возвращаемый тип закрытия async ||
- это анонимный тип, сгенерированный компилятором.
Такой тип реализует Future
и захватывает дополнительный срок жизни, связанный с областью применения закрытия async ||
. .
fn main() {
let closure = async move |data: &i32| { --+ '2 start
println!("data: {}", data); |
}; |
|
is_a(closure); |
v
}
Блок asyn c возвращает тип с подписью, например:
impl Future<Output = SomeType> + '2 + '...
Где '2
- время жизни замыкания.
Примечание что при использовании функции asyn c вместо замыкания этого дополнительного срока службы нет.
Когда вы вызываете 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
является типом, реализованным для определенного c времени жизни '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 ||
clos
Зачем нужен срок службы '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
Рассмотрим приведенный выше пример: значение - это слот памяти, выделенный в стеке, и он будет действителен до тех пор, пока не вернется my_function
и стек не размотается.
Время жизни '1
take учетная область действия value
, когда my_function
возвращает ссылку &value
не является более действительным.
Но откуда идет жизнь '2
?
Это потому что closure(&value)
возвращает сущность, которая реализует Future
, который будет жить в исполнителе времени выполнения, в данном случае в tokio executor, до тех пор, пока не закончится вычисление.
Время жизни '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
время жизни, но это не имеет отношения к пониманию этой темы.