Функция передаёт заимствованные данные в задачу многопоточного async runtime, которая может завершиться позже самой функции: какое требование к времени жизни future предотвращает висячую ссылку?
Такая future должна удовлетворять ограничению 'static: она не может содержать ссылки на данные, срок жизни которых ограничен текущим вызовом функции. Это не означает, что задача обязана выполняться вечно; требуется лишь отсутствие незащищённых заимствований, которые могут стать недействительными до уничтожения future.
Асинхронные runtime отделяют момент создания задачи от момента её выполнения. Runtime может сохранить future во внутренней очереди, передать её другому рабочему потоку или выполнить после возврата вызвавшей функции.
Обычные ссылки на стек функции для этого не подходят: после возврата функции соответствующая область памяти больше не принадлежит ей. Ограничение 'static формализует это требование на уровне типов, не позволяя runtime получить future с потенциально висячими ссылками.
Заимствованная строка, ссылка на локальную структуру или ссылка на параметр функции может быть действительна только пока существует исходное значение. Если задача сохранит такую ссылку, а функция завершится раньше задачи, обращение к ней станет некорректным.
Дополнительное требование Send решает другую задачу: оно разрешает перенос future между потоками. 'static отвечает за допустимость времени жизни, а Send — за безопасное перемещение; одно ограничение не заменяет другое.
Операция запуска долгоживущей задачи обычно требует, чтобы и сама future, и её выход имели 'static. Runtime не обязан доказывать, что задача завершится до выхода из текущей функции, поэтому он принимает только значения, не зависящие от локальных заимствований.
Безопасные варианты — передать задаче владение данными или использовать разделяемое владение с подходящим временем жизни:
Здесь future владеет клоном Arc, поэтому не зависит от локальной ссылки на переменную data. Если задача должна завершиться строго внутри текущего контекста, другой вариант — не порождать независимую задачу, а непосредственно ожидать future; тогда её заимствования могут быть ограничены областью этого ожидания.
Компромисс состоит в выборе модели владения. Перемещение String устраняет синхронизацию, но лишает вызывающий код владения; Arc сохраняет совместное владение, однако добавляет атомарный подсчёт ссылок и не делает внутреннее значение изменяемым без отдельной синхронизации.
HTTP-обработчик формирует локальную строку с параметрами запроса и хочет передать ссылку на неё в фоновую задачу. Если задача запускается независимо, ссылка может пережить обработчик, поэтому компилятор отклоняет такую конструкцию.
Вариант с копированием строки прост и изолирует задачу, но может быть дорогим для больших данных. Вариант с Arc уменьшает копирование и подходит для неизменяемых данных, но требует контролировать совместный доступ. Если работа должна закончиться до ответа HTTP, лучше ожидать future непосредственно: это сохраняет возможность заимствования и не создаёт неконтролируемую фоновую задачу.
Обычно для независимой фоновой работы выбирают владение через String, Arc или другой owned-тип. В результате задача безопасно переживает обработчик, а время жизни данных становится явным в структуре программы.
Нет. 'static у future означает, что в ней нет ссылок с меньшим временем жизни. Future может владеть обычным String, Vec или экземпляром пользовательского типа; сами эти значения будут уничтожены после завершения задачи. Статическая память требуется только для ссылок, действительно живущих всю программу, а не для любого значения, удовлетворяющего этому ограничению.
async move, чтобы любая future стала 'static?Нет. async move переносит захваченные значения во владение future, но перемещаемое значение само может содержать короткоживущую ссылку. Например, перемещение структуры со ссылкой на локальную строку не устраняет ограничение этой ссылки. Нужно передать owned-данные либо объект, содержащий только ссылки с подходящим временем жизни.
Нет. Send гарантирует, что значение можно безопасно переместить между потоками, но ничего не говорит о том, сколько будут жить содержащиеся в нём ссылки. Future может быть Send и одновременно содержать ссылку на локальную переменную, которая станет недействительной после возврата функции. Для независимой задачи обычно нужны оба свойства: 'static для времени жизни и Send для миграции между потоками.