Что именно гарантирует ограничение «тип T живёт не меньше времени 'a», если T не является ссылкой?
Ограничение T: 'a гарантирует, что тип T не содержит ссылок, способных стать недействительными до окончания времени жизни 'a. Оно не продлевает время жизни данных и не означает, что сам T является ссылочным типом.
Это ограничение нужно, когда значение типа T сохраняется или используется там, где оно должно оставаться корректным в течение 'a.
В Rust время жизни является частью правил корректности ссылок. Обобщённый код работает с неизвестным конкретным типом, поэтому компилятору недостаточно знать только его размер или реализованные трейты: иногда нужно доказать, что внутренние заимствования этого типа не станут висячими.
Ограничение T: 'a решает именно эту задачу. Оно позволяет безопасно использовать обобщённое значение в контексте, который длится не меньше 'a, без ручного перечисления всех возможных ссылок внутри T.
Предположим, функция получает значение T и помещает его в структуру, живущую до 'a. Если T может содержать ссылку на локальные данные с более коротким временем жизни, после выхода из внутренней области структура будет содержать недействительную ссылку.
Неверное решение может привести не к немедленной ошибке в месте создания значения, а к нарушению инвариантов API: вызывающий код передаст временное заимствование, а обобщённая функция сохранит его дольше разрешённого срока.
T: 'a читается как: значение типа T безопасно использовать в течение 'a. Для типов, содержащих ссылки, это означает, что соответствующие ссылки и вложенные заимствования живут достаточно долго.
Для полностью владеющих типов, например String или Vec<u8>, такое ограничение обычно выполняется независимо от внешних данных: они не заимствуют содержимое. Для типа вроде &'b str оно требует, чтобы 'b длилось не меньше 'a.
Ограничение не меняет фактическое время жизни ссылки. Если значение уже содержит ссылку с коротким временем жизни, запись T: 'a не может её «растянуть» — компилятор отвергнет такой вариант.
Минимальный пример хранения обобщённого значения:
В store значение T записывается в slot, который доступен в течение 'a. Поэтому компилятор требует доказательство T: 'a. Если T содержит ссылку на данные, живущие меньше 'a, вызов становится недопустимым.
Не следует путать T: 'a с ограничением 'a: 'b: первое относится к тому, какие заимствования может содержать тип, второе сравнивает два времени жизни. Также это не то же самое, что требование T: 'static: 'static означает отсутствие заимствований короче всей жизни программы и является значительно более сильным условием.
В библиотеке реализуют контейнер, который принимает пользовательский контекст и хранит его вместе с обработчиком до завершения операции. Контекст может быть либо владеющим объектом, либо представлением с заимствованными данными.
Вариант с требованием T: 'static прост для реализации, но запрещает безопасные краткоживущие представления и вынуждает пользователей копировать данные или использовать Arc. Вариант без ограничения не позволяет доказать корректность хранения значения.
Выбранное решение — параметризовать контейнер временем жизни операции и потребовать T: 'a. Это сохраняет поддержку заимствованных данных, но не допускает их использование после завершения соответствующего времени жизни. Если API действительно должен переживать все внешние области, тогда оправдано более сильное требование 'static.
T: 'a, что сам объект T будет существовать до 'a?Нет. Ограничение описывает допустимость внутренних заимствований типа T, а не способ хранения самого значения. Объект может быть перемещён, уничтожен или передан дальше раньше 'a; гарантируется лишь, что при использовании в контексте 'a его ссылки не будут короче этого срока.
T: 'a отличается от T: 'static?T: 'static требует, чтобы T не содержал ссылок с нестационарным временем жизни. Тип может владеть данными и поэтому удовлетворять 'static, но ссылка на локальную переменную — нет. Ограничение T: 'a слабее: оно допускает заимствования, если они живут достаточно долго для конкретного 'a.
T: 'a ограничением на конкретную ссылку внутри типа?Обычно нельзя, потому что обобщённый T может иметь любую структуру: ссылку, кортеж, коллекцию с заимствованиями или собственный тип с несколькими lifetime-параметрами. Bound T: 'a даёт обобщённую гарантию для всей структуры. Если API знает точную форму данных, можно выразить более узкие ограничения явно, но для произвольного T это ограничение является необходимым способом передать компилятору требование о вложенных заимствованиях.