Что гарантирует аннотация dyn Trait + 'a и почему она не задаёт срок жизни самого объекта?
dyn Trait + 'a означает, что данные, скрытые за объектом трейта, включая возможные ссылки внутри конкретного типа, должны быть действительны как минимум в течение 'a. Эта аннотация ограничивает время жизни заимствований внутри объекта, но не определяет, как долго сам объект будет существовать: срок жизни объекта задаётся отдельно владельцем или внешней ссылкой на него.
Rust использует проверку владения и заимствований, чтобы обеспечивать безопасность памяти без необходимости сборщика мусора. Для обычных ссылочных типов компилятору достаточно видеть их времена жизни, но trait object скрывает конкретный тип за динамической диспетчеризацией.
Скрытый тип может владеть данными или содержать ссылки. Поэтому системе типов нужно отдельно выразить, насколько долго эти внутренние заимствования должны оставаться действительными. Граница dyn Trait + 'a решает именно эту задачу.
Предположим, объект трейта реализован типом, который хранит ссылку на строку. Если такой объект поместить в Box<dyn Trait>, контейнер должен знать, допустимо ли хранение объекта с не-'static заимствованиями.
Ошибочное понимание приводит к двум типичным последствиям. Разработчик либо ожидает, что + 'a продлит жизнь объекта или строки, либо без необходимости требует 'static и тем самым отбрасывает корректные сценарии с временными заимствованиями.
'a после dyn Trait является границей времени жизни объекта трейта. Она требует, чтобы конкретный тип, скрытый за dyn Trait, был пригоден для использования в течение 'a; в частности, все ссылки, хранящиеся внутри этого типа, должны пережить 'a.
Эта граница не меняет владельца данных и не продлевает их существование. Если объект содержит ссылку на строку, строка должна быть создана раньше и прожить достаточно долго сама по себе.
В Box<dyn View + 'a> Box владеет самим объектом Page, но Page заимствует title. Поэтому коробку нельзя использовать так, будто её содержимое не зависит от 'a. При этом коробку можно уничтожить раньше окончания 'a: граница задаёт допустимый максимум использования содержащегося заимствования, а не обязательную продолжительность существования коробки.
Важно различать два времени жизни: время жизни объекта как значения и время жизни ссылок, которые он хранит. Например, ссылка на объект может иметь время жизни 'b, а сам объект может содержать данные, требующие 'a; эти параметры описывают разные отношения.
Если граница не указана явно, Rust применяет правила вывода времени жизни trait object. Они зависят от контекста: у контейнеров вроде Box<dyn Trait> отсутствие явной границы обычно приводит к требованию 'static, тогда как окружающая ссылка может связать границу объекта со своим временем жизни. В публичных сигнатурах явная запись + 'a часто делает контракт понятнее и предотвращает ошибочное неявное требование 'static.
Альтернатива — использовать dyn Trait + 'static. Это означает не «объект будет жить вечно», а то, что объект не содержит заимствований с ограниченным временем жизни. Он может владеть данными и быть уничтожен в любой момент.
Сервис принимает обработчики через Box<dyn Handler> и должен временно использовать обработчик, ссылающийся на конфигурацию, принадлежащую вызывающему коду. Неявное требование 'static у Box<dyn Handler> отвергает такой обработчик, хотя конфигурация гарантированно существует всё время работы сервиса.
Рассматриваются три варианта:
'static: сигнатура проще, но обработчики с заимствованной конфигурацией становятся непригодными;Box<dyn Handler + 'a>: сохраняется возможность заимствований, но 'a нужно последовательно провести через связанные типы и функции;'a, но увеличиваются расход памяти и стоимость копирования.Если сервис действительно не хранит обработчик дольше жизни конфигурации, выбирается явная граница Box<dyn Handler + 'a>. Она точно отражает контракт, не требует 'static и не заставляет без необходимости переходить к владению копиями.
Означает ли dyn Trait + 'a, что сам объект обязан существовать не меньше 'a?
Нет. Эта запись ограничивает пригодность типа объекта с учётом его внутренних заимствований. Сам объект может быть уничтожен раньше 'a; отдельно проверяется срок жизни конкретной ссылки на объект или срок владения контейнером.
Почему Box<dyn Trait> часто не принимает объект, содержащий ссылку на локальную переменную?
Для Box отсутствие явной границы времени жизни trait object обычно трактуется как 'static. Это требует, чтобы скрытый тип не зависел от краткоживущих заимствований. Решение — либо указать подходящую границу Box<dyn Trait + 'a>, либо сделать объект владеющим своими данными.
Может ли добавление + 'static продлить жизнь данных, на которые ссылается объект?
Нет. 'static является ограничением, а не операцией продления времени жизни. Объект должен уже не содержать ссылок, зависящих от локальных данных, либо должен владеть нужными данными; аннотация не превращает краткоживущую ссылку в долгоживущую.