Разберите ситуацию: почему будущее, созданное асинхронной функцией со ссылкой, не может безопасно пережить эту ссылку?
Будущее, возвращённое асинхронной функцией, может хранить переданную ссылку до момента завершения или уничтожения. Поэтому его время жизни ограничено временем жизни заимствованного значения: если исходное значение уничтожить раньше будущего, ссылка внутри будущего станет недействительной.
Это ограничение относится не к моменту вызова функции как таковому, а к состоянию будущего, которое может быть приостановлено между точками await.
Асинхронные функции позволяют приостанавливать вычисление, сохраняя его состояние до следующего возобновления. В Rust это состояние представлено будущим, а не потоком с отдельным сборщиком мусора.
Поскольку Rust гарантирует безопасность ссылок без сборки мусора, компилятор должен учитывать ссылки, захваченные будущим. Так сохраняется обычное правило владения: приостановленное вычисление не может использовать уже уничтоженные данные.
Рассмотрим асинхронную функцию, получающую ссылку. Вызов такой функции немедленно создаёт будущее, но выполнение её тела обычно начинается только при опросе будущего. Между созданием и опросом исходное значение может выйти из области видимости или быть перемещено.
Если бы будущее не удерживало связь с временем жизни ссылки, планировщик мог бы возобновить его после уничтожения исходных данных. Это привело бы к обращению к недействительной памяти, поэтому Rust запрещает такое размещение будущего ещё на этапе компиляции.
Параметр-ссылка асинхронной функции становится частью скрытого состояния возвращаемого будущего. Концептуально результат имеет зависимость от времени жизни входной ссылки: будущее не может жить дольше заимствования, которое оно хранит.
Вызов identity создаёт будущее, содержащее ссылку на text. Уничтожение text до уничтожения будущего нарушило бы его контракт, поэтому компилятор отклоняет программу. Если сначала уничтожить будущее или дождаться его завершения, заимствование заканчивается и text снова можно использовать.
Аннотация времени жизни не продлевает жизнь значения и не заставляет асинхронный код выполняться дольше. Она только описывает связь: состояние будущего зависит от доступности исходных данных.
Особенно заметно это при передаче будущего в задачу, которая может жить независимо от текущей функции. Требование Future + 'static означает, что будущее не должно содержать не-static заимствований; обычно для этого передают принадлежащие данные, например String, а не ссылку на локальную переменную.
Асинхронный обработчик получает ссылку на буфер запроса и передаёт созданное будущее в планировщик фоновых задач. Планировщик требует, чтобы задача могла жить независимо от текущего стека, поэтому будущее со ссылкой на локальный буфер не подходит.
Возможны три варианта:
Если обработка должна выполняться именно в фоне, выбирают владение данными: буфер перемещают внутрь задачи или будущего. Если же результат нужен сразу в текущем обработчике, предпочтительнее сохранить заимствование и дождаться будущего до уничтожения буфера.
Создаётся ли заимствование только при первом await или уже при вызове асинхронной функции?
Связь с аргументом возникает уже при создании будущего. Тело функции ещё может не выполняться, но возвращённое значение концептуально хранит переданную ссылку. Поэтому исходное значение нельзя уничтожить, пока живо это будущее.
Можно ли сделать такое будущее 'static с помощью аннотации времени жизни?
Нет. Аннотация лишь описывает существующее ограничение и не меняет владельца данных. Будущее станет совместимым с требованием 'static, если оно не содержит краткоживущих ссылок: например, если функция принимает принадлежащую строку или ссылку, которая сама действительно имеет 'static.
Что изменится, если будущее немедленно дождаться?
Пока выполняется await, будущее и его ссылки должны оставаться действительными. После завершения и уничтожения будущего связанное заимствование заканчивается, если другие заимствования не сохраняются. Поэтому исходное значение можно использовать снова после await, но нельзя безопасно уничтожить его до окончания этого ожидания.