Как правила elision работают в типе указателя на функцию с ссылками и какую связь времени жизни они выводят?
В типе указателя на функцию правила lifetime elision связывают возвращаемую ссылку с входной, если это правило однозначно применимо. Поэтому запись с одной ссылкой на входе и одной ссылкой на выходе означает функцию, которая возвращает ссылку, связанную с временем жизни входной ссылки, а не ссылку с независимым или фиксированным сроком.
При нескольких входных ссылках без явного указания времени жизни такое выведение обычно не даёт однозначной связи для результата, поэтому тип нужно записать явно.
Времена жизни являются частью типов ссылок, но явная запись всех параметров быстро сделала бы простые функции и типы указателей перегруженными синтаксисом. Elision появился как набор детерминированных правил, позволяющих опускать аннотации там, где связь ссылок однозначна и не требует догадок компилятора.
Важно, что это не общее угадывание намерений программиста. Правила применяются только в заранее определённых формах, в том числе в сигнатурах функций и типах указателей на функции.
Тип указателя на функцию — это контракт вызываемого значения. Он должен точно описывать, какие ссылки функция принимает и как срок действия результата связан со сроками действия аргументов.
Если ошибочно считать, что опущенные времена жизни независимы, можно ожидать от функции результат, переживающий входную ссылку. Если считать их связанными при наличии нескольких кандидатов, можно принять небезопасный или неоднозначный контракт.
Для типа указателя на функцию действуют те же базовые правила elision, что и для сигнатуры функции. Если у функции один ссылочный параметр, его время жизни может быть неявно присвоено возвращаемой ссылке.
Например, следующий тип:
эквивалентен типу с одной общей аннотацией времени жизни:
Это означает, что указатель должен ссылаться на функцию, работающую для любой подходящей длительности 'a. Результат может быть использован только в пределах времени жизни переданной строки.
for<'a> здесь важно понимать как универсальность контракта: тип указателя не выбирает один заранее фиксированный срок. При каждом вызове конкретная длительность определяется фактическим заимствованием.
Если ссылочных параметров несколько, одного правила может быть недостаточно. Например, у результата, возвращаемого после двух ссылочных аргументов, нет однозначного источника времени жизни. В таком случае аннотации должны явно выразить нужную связь либо тип следует изменить так, чтобы результат не заимствовал входные данные.
Elision не продлевает жизнь значения, не меняет представление указателя в памяти и не добавляет проверок во время выполнения. Это только сокращённая запись статического контракта типов.
В библиотеке требуется передавать преобразователь текста, который выбирает фрагмент из входной строки. Для некapturing-функции можно использовать указатель на функцию с неявной связью входа и результата: такой контракт точно запрещает вернуть ссылку на локальные данные преобразователя.
Вариант с полностью явным for<'a> лучше подходит для публичного API, когда связь должна быть очевидна читателю. Его минус — более многословная запись. Вариант с elision короче и безопасен, если связь однозначна; выбранное решение с elision обычно предпочтительно для локального или простого публичного типа, а явная запись — для сложного контракта, где важно убрать двусмысленность.
Если преобразователь должен захватывать состояние, указатель на функцию не подходит: он не описывает захватываемое окружение. Тогда применяют тип замыкания с соответствующими bound, но это уже другой контракт и не должно маскироваться под обычный указатель на функцию.
fn(&str) -> &str, что результат может ссылаться на данные, не связанные с аргументом?Нет. При единственном ссылочном входе elision связывает время жизни результата с этим входом. Функция не может через такой тип вернуть ссылку на локальное значение или на данные, срок жизни которых короче результата.
fn(&str) -> &str отличается от указателя на функцию с одним заранее выбранным временем жизни?Он не фиксирует конкретную длительность. Неявная запись соответствует универсальному контракту, применимому к разным вызовам с разными заимствованиями. Поэтому один и тот же указатель можно использовать для короткого и более длительного заимствования, если сама функция удовлетворяет этому контракту.
Результат с одной ссылкой должен иметь одно конкретное время жизни или явно заданное отношение между несколькими временами жизни. Простое объединение двух независимых сроков не определяет, какой из них ограничивает результат; безопасным выбором становится срок, не превышающий оба, но такие отношения не выводятся этим правилом автоматически. Разработчик должен выразить контракт явно или изменить возвращаемый тип.