Разберите ситуацию: почему функция, возвращающая замыкание, должна связывать время жизни замыкания с захваченной ссылкой?
Замыкание, захватившее ссылку, фактически содержит эту ссылку. Поэтому оно не может существовать дольше, чем значение, на которое ссылается. Связь времён жизни в возвращаемом типе сообщает компилятору: замыкание и ссылки, которые оно возвращает, допустимы только в пределах времени жизни исходного заимствования.
В Rust время жизни нужно указывать там, где из типа должна быть явно видна связь между заимствованными данными. Это позволяет проверять безопасность ссылок на этапе компиляции без сборщика мусора и без скрытого продления срока существования объектов.
Для обычной функции компилятор часто выводит связи времён жизни по правилам lifetime elision. У возвращаемого замыкания появляется дополнительный объект: само замыкание хранит захваченные значения, поэтому нужно учитывать не только результат его вызова, но и срок существования самого замыкания.
Если вернуть замыкание, захватившее ссылку на локальные данные, вызывающий код может попытаться сохранить это замыкание после уничтожения исходного значения. Тогда замыкание содержало бы недействительную ссылку.
Аннотация времени жизни не продлевает существование исходного значения. Она лишь выражает ограничение: пока замыкание доступно, заимствованный объект должен оставаться живым.
В возвращаемом типе обычно выражают две связи:
Минимальный пример:
Параметр 'a связывает входную ссылку с результатом вызова. Ограничение + 'a относится к возвращаемому типу замыкания: объект замыкания не должен пережить 'a, поскольку он хранит захваченную ссылку.
Ключевое слово move здесь переносит в замыкание саму ссылку &'a str, но не значение String. Оно не делает ссылку 'static и не перемещает объект, на который она указывает.
Альтернативой было бы вернуть замыкание, владеющее собственными данными, например скопированной строкой. Такой вариант устраняет зависимость от внешнего времени жизни, но может потребовать дополнительного выделения памяти или копирования. Требование 'static было бы ещё строже: оно разрешило бы только замыкания, не зависящие от краткоживущих заимствований, поэтому для обычной ссылки на локальные данные это неподходящее ограничение.
Допустим, библиотека принимает текстовый буфер и должна вернуть отложенный обработчик чтения. Возможны три решения:
'static. Это упрощает хранение обработчика, но запрещает полезные сценарии с временными буферами.Если обработчик используется только во время жизни буфера, выбирают первый вариант и явно связывают время жизни замыкания с входной ссылкой. В результате компилятор отклонит попытку вынести обработчик за пределы области, где буфер ещё существует, предотвращая висячую ссылку без runtime-проверок.
Вопрос: Делает ли move возвращаемое замыкание независимым от времени жизни захваченной ссылки?
Ответ: Нет. move определяет способ захвата: ссылка копируется внутрь замыкания как значение типа &'a T. Сам объект T при этом не становится собственностью замыкания. Поэтому замыкание всё ещё зависит от 'a; независимым оно станет только при захвате собственных данных, а не ссылки на внешнее значение.
Вопрос: Почему недостаточно связать с 'a только ссылку, возвращаемую вызовом замыкания?
Ответ: Потому что до вызова замыкание уже хранит захваченную ссылку. Если разрешить самому объекту замыкания пережить 'a, его можно было бы вызвать после уничтожения исходных данных. Ограничение + 'a связывает время жизни объекта замыкания с его содержимым, а -> &'a str описывает время жизни результата вызова.
Вопрос: Можно ли заменить заимствование владением без изменения поведения программы?
Ответ: Иногда можно, но это меняет семантику и стоимость решения. Замыкание может владеть String или другим объектом и тогда не зависит от внешнего заимствования, однако данные могут быть перемещены, клонированы или размещены отдельно в куче. Если вызывающему нужен доступ именно к актуальному внешнему буферу без копирования, переход к владению уже не является эквивалентной заменой.