Как трактовать два разных параметра времени жизни в типе результата функции, если оба связаны с входными ссылками?
Два разных параметра времени жизни означают, что сроки действия соответствующих ссылок независимы: функция не требует, чтобы они совпадали или имели заранее заданное отношение. При этом каждая ссылка в результате остаётся действительной только столько, сколько действительна связанная с ней входная ссылка.
Оба параметра могут получить одну и ту же конкретную длительность при вызове, но это будет следствием фактического кода, а не требованием сигнатуры.
В Rust времена жизни нужны для статической проверки заимствований без сборщика мусора. Явные параметры времени жизни позволяют описывать связи между ссылками в типах и предотвращать использование ссылки после уничтожения исходного значения.
Подход отделяет две задачи: владение данными и описание допустимых отношений между заимствованиями. Благодаря этому функция может возвращать ссылки, не копируя данные и не принимая на себя владение ими.
Рассмотрим результат, содержащий две ссылки: одна указывает на данные из первого аргумента, другая — из второго. Если ошибочно обозначить обе ссылки одним параметром времени жизни, сигнатура создаст дополнительное ограничение: оба заимствования должны удовлетворять одной общей области действия.
Если использовать разные параметры, функция выражает более точную связь. Уничтожение данных, на которые указывает первая ссылка, не должно автоматически ограничивать вторую ссылку, если результат используется раздельно и правила заимствования это допускают.
Разные параметры времени жизни — например, 'a и 'b — являются разными обозначениями областей действия. Они не означают, что одна область обязательно длиннее другой, и не означают, что области обязательно различаются по фактической длительности.
Минимальный пример:
Здесь поле first связано с первым аргументом через 'a, а second — со вторым через 'b. Компилятор может вывести для 'a и 'b одну и ту же область действия, если оба аргумента живут одинаково долго, но сама сигнатура этого не требует.
Главное практическое следствие: Pair<'a, 'b> нельзя использовать после окончания 'a, если обращение требует first; окончание 'b аналогично ограничивает second. Конкретное место, где прекращается допустимое использование всего значения, зависит от того, какие его поля используются и какие ограничения накладывает контекст.
Единый параметр, например Pair<'a, 'a>, был бы сильнее: он связывает обе ссылки с одной областью действия. Это может сделать тип менее гибким и привести к ненужному ограничению вызывающего кода. Однако иногда единая длительность действительно нужна, если корректность алгоритма требует, чтобы обе ссылки имели общее отношение времени жизни.
Представим парсер, который возвращает структуру с фрагментом входного документа и фрагментом временного буфера. Входной документ живёт долго, а буфер может быть уничтожен сразу после операции.
Вариант с одной аннотацией времени жизни проще записать, но он связывает оба фрагмента с одной областью действия. Плюс — более простая сигнатура; минус — вызывающий код может получить ограничение, не требуемое реальными зависимостями данных.
Вариант с двумя параметрами точно отражает происхождение ссылок: фрагмент документа связан с 'a, а фрагмент буфера — с 'b. Это немного усложняет объявление типа, зато сохраняет независимость ограничений и позволяет компилятору проверять каждое поле отдельно.
Выбирают второй вариант, если ссылки действительно не зависят друг от друга. Если же один фрагмент логически обязан использоваться только вместе с другим, единая аннотация точнее выражает контракт.
Нет. Разные имена обозначают независимые параметры, но при конкретном вызове оба могут быть выведены как одна и та же область действия. Независимость означает отсутствие требования совпадения, а не обязательное различие.
Только в части, не использующей ссылку на уничтоженный аргумент. Само значение результата всё ещё может находиться в памяти, но обращение к полю, связанному с завершившимся временем жизни, будет запрещено. Если контекст требует использовать всю структуру как единое значение, компилятор может применить наиболее строгие ограничения, необходимые для этого использования.
Один параметр нужен, когда контракт функции требует общей области действия или общего отношения между ссылками. Если такого требования нет, два параметра обычно точнее: они не создают искусственной зависимости между аргументами. Это не вопрос предпочтения синтаксиса — аннотация должна отражать реальную зависимость корректности результата от входных заимствований.