Как правило вывода времени жизни различает Box<dyn Trait> и &dyn Trait без явного lifetime-bound?
Для &dyn Trait время жизни объекта-трейта обычно выводится из времени жизни самой ссылки. Для владеющего Box<dyn Trait> автоматическое правило по умолчанию обычно выбирает 'static, поэтому сохранённый внутри объект не должен содержать заимствований с более коротким временем жизни, если bound не указан явно.
Это правило относится к времени жизни заимствований внутри скрытого конкретного типа, а не к длительности существования самого Box или ссылки.
Trait objects появились как механизм динамического полиморфизма: код может работать с разными реализациями трейта через единый интерфейс, не зная конкретного типа. Когда конкретный тип скрыт, компилятору всё равно нужно определить, как долго допустимы заимствования, содержащиеся в этом объекте.
Явные lifetime-bound позволяют безопасно хранить trait object, связанный с данными вызывающего кода. Для распространённых владеющих контейнеров выбран консервативный default 'static, чтобы объект можно было безопасно перемещать и хранить без скрытой зависимости от локального заимствования.
Рассмотрим API, который возвращает или хранит Box<dyn Trait>. Конкретная реализация трейта может содержать ссылку на внешние данные. Если библиотека позволит такому объекту жить дольше этих данных, возникнет потенциальное обращение к уже уничтоженной памяти.
Противоположная ошибка тоже существенна: без необходимости требовать 'static API станет менее гибким и не сможет принимать безопасные объекты, заимствующие данные только на ограниченный срок.
В ссылочном trait object запись &'a dyn Trait связывает lifetime объекта с lifetime внешней ссылки. Поэтому объект может заимствовать данные на срок 'a, но не дольше. Часто этот lifetime выводится компилятором из контекста.
Для Box<dyn Trait> контейнер владеет самим trait object, но не обязан владеть данными, на которые ссылается его скрытый тип. Поэтому в отсутствии другого контекста для dyn Trait внутри Box применяется bound 'static. Это означает: скрытый тип не содержит ссылок, действующих меньше 'static; он может владеть данными или содержать только 'static-заимствования.
Если нужно хранить заимствующий объект, lifetime у trait object следует сделать параметром внешнего типа или функции:
Здесь Holder<'a> не может пережить данные, доступные через 'a. Важно отличать dyn Readable + 'a от lifetime метода: bound ограничивает заимствования, хранящиеся внутри динамического объекта, а не запрещает вызовы методов после произвольного момента.
Правило вывода зависит от контекста и содержащего типа. Нельзя механически утверждать, что любой dyn Trait по умолчанию всегда имеет 'static; для ссылок и некоторых обобщённых контейнеров lifetime может выводиться из внешних ограничений.
Допустим, обработчик должен временно хранить представление строки. Вариант с Box<dyn Readable> без lifetime-bound потребует 'static и не примет представление, заимствующее локальную строку. Вариант с Box<dyn Readable + 'a> сохраняет возможность такого заимствования, но заставляет весь контейнер явно не выходить за пределы 'a.
Можно было бы вместо этого хранить конкретный тип View<'a>. Такой вариант эффективнее и сохраняет статическую диспетчеризацию, но связывает API с одной реализацией. Trait object дороже по косвенности вызова и обычно требует таблицы виртуальных методов, зато позволяет менять реализации независимо от контейнера.
Выбранное решение — Holder<'a> с Box<dyn Readable + 'a>, если контейнер действительно должен принимать разные реализации. Результат: API сохраняет полиморфизм и одновременно получает проверяемую компилятором гарантию, что заимствованные данные живут достаточно долго.
dyn Trait + 'static, что объект обязан жить до завершения программы?Нет. Это означает, что объект не содержит заимствований с lifetime короче 'static. Сам объект может быть уничтожен сразу после создания. Например, Box<dyn Trait + 'static> может владеть строкой и быть освобождённым через секунду; 'static относится к допустимым внутренним заимствованиям, а не к фактическому времени жизни значения.
Box<dyn Trait + 'a> на Box<&'a dyn Trait>?Нет, это разные конструкции. Box<&'a dyn Trait> владеет ссылкой, а сам динамический объект находится вне Box. Box<dyn Trait + 'a> владеет значением конкретного типа, которое может содержать заимствования на 'a. Первая форма добавляет уровень ссылочной косвенности и имеет другие требования к типу, тогда как вторая является обычным владеющим trait object с ограниченным lifetime.
Lifetime отвечает только за длительность допустимых заимствований. Он не гарантирует отсутствие гонок данных, потокобезопасность или неизменяемость состояния. Для передачи объекта между потоками могут дополнительно потребоваться Send и Sync, а для безопасных мутабельных операций — соответствующая модель владения и синхронизации.