Подтвердить что ты не робот

Почему имя жизни отображается как часть типа функции?

Я считаю, что это объявление функции сообщает Rust, что время жизни выхода функции совпадает с временем жизни его параметра s:

fn substr<'a>(s: &'a str, until: u32) -> &'a str;
         ^^^^

Мне кажется, что компилятор должен знать это только (1):

fn substr(s: &'a str, until: u32) -> &'a str;

Что означает аннотация <'a> после названия функции? Зачем нужен компилятор и что с ним делать?


(1): Я знаю, что он должен знать еще меньше, из-за пожизненного решения. Но этот вопрос связан с явным определением времени жизни.

4b9b3361

Ответ 1

Позвольте мне рассказать о предыдущих ответах...

Что означает аннотация < 'a > после названия функции?

Я бы не использовал для этого слово "аннотация". Подобно <T> вводится общий тип параметра, <'a> вводит общий параметр времени жизни. Вы не можете использовать какие-либо общие параметры, не вводя их в первую очередь, а для общих функций это введение происходит сразу после их имени. Вы можете представить себе общую функцию как семейство функций. Таким образом, по сути, вы получаете одну функцию для каждой комбинации общих параметров. substr::<'x> будет конкретным членом этого семейства функций для некоторого времени жизни 'x.

Если вы неясны, когда и почему мы должны быть явными в отношении сроков жизни, читайте дальше...

Параметр lifetime всегда связан со всеми ссылочными типами. Когда вы пишете

fn main() {
    let x = 1729;
    let r = &r;
}

компилятор знает, что x живет в области основной функции, заключенной в фигурные скобки. Внутри он идентифицирует эту область действия с некоторым параметром времени жизни. Для нас это неназванное. Когда вы берете адрес x, вы получите значение определенного ссылочного типа. Тип ссылки является видом члена двумерного семейства ссылочных типов. Одна ось - тип, на который ссылаются опорные точки, а другая ось - это время жизни, которое используется для двух ограничений:

  • Параметр lifetime ссылочного типа представляет собой верхнюю границу того, как долго вы можете удерживать эту ссылку
  • Параметр lifetime ссылочного типа представляет собой нижнюю границу для времени жизни вещей, на которые вы можете сделать контрольную точку.

Вместе эти ограничения играют жизненно важную роль в истории безопасности памяти Rust. Цель здесь - избегать оборванных ссылок. Мы хотели бы исключить ссылки, которые указывают на некоторую область памяти, которую нам больше не разрешают использовать, потому что эта вещь, к которой она обращалась, больше не существует.

Одним из потенциальных источников замешательства является, вероятно, тот факт, что параметры жизни в большинстве случаев невидимы. Но это не значит, что их там нет. В их типе всегда есть параметр продолжительности жизни. Но такой параметр lifetime не должен иметь имя и большую часть времени нам не нужно упоминать об этом, потому что компилятор может автоматически назначать имена для параметров времени жизни. Это называется "пожизненная элита". Например, в следующем случае вы не видите никаких параметров времени жизни:

fn substr(s: &str, until: u32) -> &str {…}

Но все нормально писать так. Это фактически короткий синтаксис для более явного

fn substr<'a>(s: &'a str, until: u32) -> &'a str {…}

Здесь компилятор автоматически присваивает одно и то же имя "времени жизни" и "времени жизни", потому что это очень распространенный шаблон и, скорее всего, именно то, что вы хотите. Поскольку этот шаблон настолько распространен, компилятор позволяет нам уйти, не говоря ничего о жизни. Он предполагает, что эта более явная форма - это то, что мы имели в виду на основе нескольких правил "пожизненного разрешения" (которые, по крайней мере, документированы здесь)

Существуют ситуации, когда явные параметры времени жизни не являются необязательными. Например, если вы пишете

fn min<T: Ord>(x: &T, y: &T) -> &T {
    if x <= y {
        x
    } else {
        y
    }
}

компилятор будет жаловаться, потому что он будет интерпретировать вышеуказанное объявление как

fn min<'a, 'b, 'c, T: Ord>(x: &'a T, y: &'b T) -> &'c T { … }

Итак, для каждой ссылки вводится отдельный параметр времени жизни. Но никакая информация о том, как параметры жизни связаны друг с другом, доступна в этой сигнатуре. Пользователь этой универсальной функции может использовать любое время жизни. И это проблема внутри его тела. Мы пытаемся вернуть либо x, либо y. Но тип x равен &'a T. Это несовместимо с типом возврата &'c T. То же самое верно для y. Поскольку компилятор ничего не знает о том, как эти времена жизни относятся друг к другу, небезопасно возвращать эти ссылки в качестве ссылки типа &'c T.

Можно ли безопасно перейти от значения типа &'a T в &'c T? Да. Это безопасно, если срок службы 'a равен или больше времени жизни 'c. Или, другими словами, 'a: 'c. Итак, мы могли бы написать это

fn min<'a, 'b, 'c, T: Ord>(x: &'a T, y: &'b T) -> &'c T
      where 'a: 'c, 'b: 'c
{ … }

и уйти с ним без компилятора, жалующегося на тело функции. Но это на самом деле излишне сложно. Мы можем просто написать

fn min<'a, T: Ord>(x: &'a T, y: &'a T) -> &'a T { … }

и используйте один параметр времени жизни для всего. Компилятор может вывести 'a как минимальное время жизни ссылок на аргументы на сайте вызова только потому, что мы использовали одно и то же имя жизни для обоих параметров. И это время жизни именно то, что нам нужно для возвращаемого типа.

Надеюсь, это ответит на ваш вопрос.:) Ура!

Ответ 2

Что означает аннотация < 'a > после названия функции?

fn substr<'a>(s: &'a str, until: u32) -> &'a str;
         ^^^^

Это общий параметр времени жизни. Он похож на общий параметр типа (часто рассматривается как <T>), в котором вызывающий функции получает решение о том, что такое время жизни. Как вы сказали, время жизни результата будет таким же, как и время жизни первого аргумента.

Все имена жизни эквивалентны, за исключением одного: 'static. Эта продолжительность жизни заранее задана так, чтобы она "гарантировала жизнь на всю жизнь программы".

Наиболее частое имя параметра lifetime, вероятно, 'a, но вы можете использовать любую букву или строку. Одиночные буквы являются наиболее распространенными, но любой snake_case идентификатор является приемлемым.

Зачем ему нужен компилятор и что с ним делать?

Ржавчина в целом способствует тому, чтобы вещи были явными, если не было очень хорошей эргономической выгоды. Для сроков жизни пожизненный elision заботится о чем-то вроде 85 +% случаев, что казалось очевидной победой.

Параметры типа живут в том же пространстве имен, что и другие типы - это T общий тип или кто-то назвал структуру, которая? Таким образом, параметры типа должны иметь явное аннотирование, которое показывает, что T является параметром, а не реальным типом. Однако параметры времени жизни не имеют такой же проблемы, поэтому не причина.

Вместо этого основное преимущество явно перечисления параметров типа заключается в том, что вы можете контролировать взаимодействие нескольких параметров. Пример бессмыслицы:

fn better_str<'a, 'b, 'c>(a: &'a str, b: &'b str) -> &'c str
    where 'a: 'c,
          'b: 'c,
{
    if a.len() < b.len() { a } else { b }
}

У нас есть две строки и говорят, что входные строки могут иметь разные времена жизни, но должны оба переживать время жизни значения результата.

Другой пример: указанный DK, состоит в том, что структуры могут иметь свои собственные времена жизни. Я сделал этот пример также немного глупости, но, надеюсь, передает следующее:

struct Player<'a> {
    name: &'a str,
}

fn name<'p, 'n>(player: &'p Player<'n>) -> &'n str {
    player.name
}

Времена жизни могут быть одной из наиболее размытых частей Rust, но они очень хороши, когда вы начинаете их понимать.

Ответ 3

Аннотация <'a> просто объявляет времена жизни, используемые в функции, точно так же, как общие параметры <T>.

fn subslice<'a, T>(s: &'a [T], until: u32) -> &'a [T] { \\'
    &s[..until as usize]
}

Обратите внимание, что в вашем примере можно определить все сроки жизни.

fn subslice<T>(s: &[T], until: u32) -> &[T] {
    &s[..until as usize]
}

fn substr(s: &str, until: u32) -> &str {
    &s[..until as usize]
}

пример детской игры