Почему средство проверки заимствований нуждается в метках времени жизни для вывода, когда входные данные очень ясны? - PullRequest
0 голосов
/ 09 января 2019

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

fn main() {
    let ss = "abc"; // lets say 'a scope
    let tt = "def"; // lets say 'b scope
    let result = func(ss, tt);
}    

fn func(s: &str, t: &str) -> &str {
    t
}
| fn func(s: &str, t: &str) -> &str {
|                              ^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `s` or `t`

Почему вообще важно, что происходит в этом коде? Я что-то упустил очень важный крайний случай?

но когда я отмечаю их тегами времени жизни, это работает.

fn func<'a>(s: &'a str, t: &'a str) -> &'a str {
    t
}

Я прочитал, что каждая привязка переменной (пусть) создает неявную область видимости, тогда как 2 входные переменные имеют одинаковую область действия. Поправь меня, если я работаю. В стеке вызова функции func, сначала будет нажата «s», а затем «t», поэтому «s» и «t» имеют разные времена жизни. Сначала удаляется буква «t», а затем «s».

Ответы [ 2 ]

0 голосов
/ 09 января 2019

Если я правильно понимаю, вопрос в том, "почему оба аргумента могут иметь одинаковое время жизни?" Короткий ответ заключается в том, что аннотации времени жизни - это не конкретные значения, а bounds - в нем говорится, что «это значение должно жить не больше / не меньше этого времени жизни».

Когда вы пишете свой код так же, как и в вопросе: fn func<'a>(s: &'a str, t: &'a str) -> &'a str, вы буквально говорите следующее:

  • есть время жизни - назовем его 'a, которое может быть разным на каждом сайте вызова.
  • аргументы s и t должны оба жить не менее затем 'a (для строковых литералов это всегда так, поскольку они 'static, но это может не выполняться для &String приведен к &str) - то есть тип функции противопоказан вариант по сравнению с типами аргументов (и время жизни является частью типа).
  • возвращаемое значение должно сохраняться не более затем 'a - тип функции co вариант с возвращаемым типом.

(для получения дополнительной информации о дисперсии см. Rustonomicon )

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

fn main() {
    let ss = "abc";
    let mut result = "";
    {
        let tt = "def".to_string();
        result = func(ss, &tt);
    }
    println!("{}", result);
}    

fn func<'a>(s: &'a str, t: &'a str) -> &'a str {
    s
}

( детская площадка )

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

fn func<'a, 'b>(s: &'a str, t: &'b str) -> &'a str {
    s
}

... он компилирует и возвращает желаемый результат (хотя и с некоторыми предупреждениями), поскольку теперь время жизни 'b не связано с 'a и, фактически, может быть вообще удалено - жизненное значение elision сделает его работа хорошо.

0 голосов
/ 09 января 2019

Вы не сообщили компилятору, может ли возвращаемое значение заимствовать из s, из t, из обоих или из ни одного:

fn from_s<'a, 'b>(s: &'a str, t: &'b str) -> &'a str {
    // can be abbreviated: fn from_s<'a>(s: &'a str, t: &str) -> &'a str
    s
}

fn from_t<'a, 'b>(s: &'a str, t: &'b str) -> &'b str {
    // can be abbreviated: fn from_t<'a>(s: &str, t: &'a str) -> &'a str
    t
}

fn from_both<'a>(s: &'a str, t: &'a str) -> &'a str {
    if s < t {
        s
    } else {
        t
    }
}

fn from_neither<'a, 'b>(s: &'a str, t: &'b str) -> &'static str {
    // can be abbreviated: fn func(s: &str, t: &str) -> &'static str
    "foo"
}

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

Чтобы понять, почему разница будет иметь значение, рассмотрим такой абонент, как

fn main() {
    let s = String::from("s");
    let r;
    {
        let t = String::from("t");
        r = from_s(&s, &t);
        // t goes out of scope
    }
    println!("{}", r);
}

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

...