Зачем при объявлении обобщённого ассоциированного типа Rust требует связать его параметр времени жизни с Self через where Self: 'a?
Связь where Self: 'a сообщает Rust, что тип Self должен быть действителен не меньше времени жизни 'a. Это необходимо, чтобы обобщённый ассоциированный тип мог безопасно описывать данные, заимствованные из конкретного экземпляра Self.
Без такого ограничения параметр 'a у ассоциированного типа был бы независим от времени жизни самого объекта. Тогда тип мог бы быть инстанцирован с 'a, превышающим срок существования Self, что нарушило бы безопасность ссылок.
Обычные ассоциированные типы не могли выразить связь между временем жизни заимствования и параметром метода. Обобщённые ассоциированные типы решили эту проблему: один тип может зависеть от конкретного времени жизни, выбранного при использовании.
Однако такая гибкость требует явного описания отношений между временем жизни. Rust не предполагает автоматически, что произвольный параметр 'a ограничен временем жизни экземпляра, потому что это не следует из одного факта существования GAT.
Рассмотрим трейт, возвращающий представление данных, заимствованных из self. Возвращаемый тип должен оставаться действительным только пока действителен объект, из которого получено заимствование.
Если связь Self: 'a не выражена, компилятор не может считать тип Self::Ref<'a> корректным для любого 'a. Параметр 'a может обозначать срок, который начинается раньше или заканчивается позже, чем допустимо для конкретного экземпляра Self.
Важно, что это не продлевает жизнь объекта. Ограничение лишь запрещает использовать ассоциированный тип в контексте, где Self уже не может существовать достаточно долго.
Запись Self: 'a означает: Self outlives 'a, то есть все данные, необходимые для существования значения типа Self, действительны как минимум в течение 'a. Для типа, содержащего ссылку, это ограничивает 'a сроком жизни вложенных заимствований.
Минимальный пример:
Вызов view выбирает конкретное 'a, соответствующее заимствованию self. Реализация связывает Self::Ref<'a> с &'a str, поэтому результат не может пережить объект Text.
Ограничение размещается в объявлении GAT, потому что оно является частью его общей корректности. Каждая реализация должна учитывать это условие, а пользователь трейта получает гарантию, что применение Self::Ref<'a> допустимо только при выполнении связи Self: 'a.
Это отличается от записи времени жизни у обычной ссылки. Аннотация &'a T ограничивает конкретную ссылку, а where Self: 'a задаёт отношение между временем жизни параметра GAT и самим типом-владельцем. Rust не выводит такое отношение только из предполагаемого смысла метода.
Альтернатива — возвращать конкретную ссылку напрямую, например &'a str. Это проще, но теряет возможность скрыть разные типы представлений за ассоциированным типом. Возврат владеющего значения устраняет связь с временем жизни, но обычно требует копирования, выделения памяти или иной передачи владения.
В библиотеке есть трейт для просмотра содержимого различных контейнеров. Для одного контейнера результатом может быть &str, для другого — срезом байтов, а для третьего — специальным представлением, заимствующим внутреннее состояние.
Рассматривались три варианта. Возврат Box<dyn ViewData> скрывал бы различия типов, но добавлял динамическую диспетчеризацию и потенциальное выделение памяти. Возврат владеющей копии упрощал бы времена жизни, но ухудшал производительность. Использование GAT сохраняло статическую диспетчеризацию и позволяло каждому контейнеру выбрать собственный тип результата.
Выбрали GAT с where Self: 'a, потому что результат напрямую заимствует данные из контейнера. В итоге компилятор проверяет, что представление не используется после уничтожения контейнера, а библиотека сохраняет обобщённость без обязательного копирования.
Self: 'a, что объект Self будет жить дольше 'a автоматически?Нет. Это ограничение проверяет уже существующую длительность жизни, но не продлевает её и не предотвращает уничтожение объекта. Если объект локальный, его нельзя вернуть из функции только потому, что в типе присутствует Self: 'a.
&'a self в сигнатуре метода?&'a self действительно связывает конкретное заимствование self с 'a. Но объявление GAT должно быть корректным независимо от конкретного метода и конкретной реализации. where Self: 'a делает необходимое отношение частью контракта самого ассоциированного типа, чтобы любое использование Self::Ref<'a> проходило проверку корректности типов.
Реализация может выбрать конкретную форму Self::Ref<'a>, но обязана соблюдать объявленные ограничения и совпадать с требуемым параметром времени жизни. Например, &'a str допустим для GAT, возвращающего представление, зависящее от 'a; тип без соответствующей связи или результат, способный пережить Self вопреки контракту, не сделает реализацию безопасной.