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

Я пытался разобраться в модели заимствования и владения Rust.

Предположим, у нас есть следующий код:

fn main() {
    let a = String::from("short");
    {
        let b = String::from("a long long long string");
        println!("{}", min(&a, &b));
    }
}

fn min<'a>(a: &'a str, b: &'a str) -> &'a str {
    if a.len() < b.len() {
        return a;
    } else {
        return b;
    }
}

min() просто возвращает ссылку на более короткую из двух указанных строк. main() передает две строковые ссылки, чьи ссылки определены в разных областях. Я использовал String::from() так что ссылки не имеют статического времени жизни. Программа правильно печатает short, Вот пример на Rust Playground.

Если мы ссылаемся на Rustonomicon (который я оцениваю как документ в стадии разработки), нам говорят, что значение сигнатуры функции, например:

fn as_str<'a>(data: &'a u32) -> &'a str

означает функцию:

берет ссылку на u32 с некоторой продолжительностью жизни, и обещает, что он может дать ссылку на str это может жить так же долго.

Теперь давайте обратимся к подписи min() из моего примера:

fn min<'a>(a: &'a str, b: &'a str) -> &'a str

Это более запутанно, так как:

  • У нас есть две входные ссылки.
  • Их референты определены в разных областях, что означает, что они действительны для разных времен жизни (a действует дольше).

Используя формулировку, аналогичную приведенной выше формулировке, что означает функция подписи min() имею в виду?

  1. Функция принимает две ссылки и обещает создать ссылку на str которые могут жить так долго, как референты a а также b ? Это как-то неправильно, как будто мы возвращаем ссылку на b от min() тогда ясно, что ссылка недействительна a в main(),

  2. The function accepts two references and promises to produce a reference to a str that can live as long as the shorter of the two referents of a а также b ? That could work, since both referents of a а также b remain valid in the inner scope of main(),

  3. Something else entirely?

To summarise, I don't understand what it means to bind the lifetimes of the two input references of min() to the same lifetime when their referents are defined in different scopes in the caller.

3 ответа

Решение

Это (2): возвращаемая ссылка живет столько же, сколько и более короткое время жизни ввода.

Однако, с точки зрения функции, оба времени жизни входа на самом деле одинаковы (оба 'a). Итак, учитывая, что переменная a от main() явно живет дольше, чем b, как это работает?

Хитрость заключается в том, что вызывающая сторона сокращает время жизни одной из двух ссылок, чтобы соответствовать min() подпись функции s. Если у вас есть ссылка &'x T, вы можете преобразовать его в &'y T тогда и только тогда 'x переживет 'y (также написано: 'x: 'y). Это имеет интуитивный смысл (мы можем сократить срок действия ссылки без плохих последствий). Компилятор выполняет это преобразование автоматически. Так что представьте, что компилятор превращает ваш main() в:

let a = String::from("short");
{
    let b = String::from("a long long long string");

    // NOTE: this syntax is not valid Rust! 
    let a_ref: &'a_in_main str = &a;
    let b_ref: &'b_in_main str = &b;
    println!("{}", min(&a as &'b_in_main str, &b));
    //                    ^^^^^^^^^^^^^^^^^^
}

Это связано с тем, что называется подтипом, и вы можете прочитать больше об этом в этом превосходном ответе.

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

Я собираюсь пойти (3) что-то еще!

С вашей подписью функции:

fn min<'a>(a: &'a str, b: &'a str) -> &'a str { ...}

// ...
min(&a, &b)

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

Некоторые примеры:

let mut a = String::from("short");
{
    let mut b = String::from("a long long long string");
    // a and b borrowed for the duration of the println!()
    println!("{}", min(&a, &b));
    // a and b borrowed for the duration of the expression, but not
    // later (since l is not a reference)
    let l = min(&a, &b).len();

    {
        // borrowed for s's scope
        let s = min(&a, &b);
        // Invalid: b is borrowed until s goes out of scope
        // b += "...";
    }
    b += "...";  // Ok: b is no longer borrowed.
    // Borrow a and b again to print:
    println!("{}", min(&a, &b));
}

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

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

Помимо того, что @Lukas упомянул в ответе, вы также можете прочитать сигнатуру функции как - Возвращенная ссылка действительна до момента, когда обе переданные ссылки являются действительными, т.е. это соединение (или AND) между временем жизни параметров.

В этом есть что-то большее. Ниже приведены два примера кода:

    let a = String::from("short");
    {
        let c: &str;
        let b = String::from("a long long long string");
        c = min(&a, &b);

    } 

А ТАКЖЕ

let a = String::from("short");
    {
        let b = String::from("a long long long string");
        let c: &str;
        c = min(&a, &b);

    }

Первый не работает (второй работает). Может показаться, что оба b а также c имеют то же время жизни, что и в той же области, но порядок в области также имеет значение, как в первом случае b жизнь закончится раньше c,

Другие вопросы по тегам