Изменяют ли аннотации времени жизни в Rust время жизни переменных? - PullRequest
3 голосов
/ 14 марта 2019

Глава Rust утверждает, что аннотации не влияют на время жизни переменной, но насколько это верно? Согласно книге, функция longest принимает две ссылки на строки и возвращает более длинную. Но здесь в случае ошибки

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let string1 = String::from("long string is long");
    let result;
    {
        let string2 = String::from("xyz");
        result = longest(string1.as_str(), string2.as_str());
    }
    println!("The longest string is {}", result);
}

это действительно изменяет время жизни переменной результата, не так ли?

Мы сказали Русту, что время жизни ссылки, возвращаемой самой длинной функцией, совпадает с меньшим из времени жизни ссылки, переданной.

Ответы [ 2 ]

6 голосов
/ 14 марта 2019

В книге просто говорится, что параметр времени жизни функции не может влиять на время жизни затронутого значения.Они не могут заставить значение жить дольше (или наоборот), чем то, что уже предписано программой.

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

Например, учитывая определение

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

время жизни двух входных ссылок должно совпадать.Тем не менее, мы можем написать это:

let local = "I am local string.".to_string();

longest(&local, "I am &'static str!");

Строковый литерал, имеющий время жизни 'static, совместим со временем жизни 'a, в этом случае он в основном ограничен строкой local.

Аналогично, в приведенном выше примере время жизни 'a должно быть ограничено вложенной строкой string2, в противном случае его нельзя было бы передать ссылкой на функцию.Это также означает, что ссылка на выход ограничена этим временем жизни, поэтому код не может скомпилироваться при попытке использовать вывод longest вне области действия string2:

error[E0597]: `string2` does not live long enough
  --> src/main.rs:14:44
   |
14 |         result = longest(string1.as_str(), string2.as_str());
   |                                            ^^^^^^^ borrowed value does not live long enough
15 |     }
   |     - `string2` dropped here while still borrowed
16 |     println!("The longest string is {}", result);
   |                                          ------ borrow later used here

См. Такжеэтот вопрос для расширенного объяснения времен жизни и их ковариационных / контравариантных характеристик:

4 голосов
/ 14 марта 2019

Во-первых, важно понимать разницу между временем жизни и областью действия.У ссылок есть время жизни, которые зависят от областей действия переменных, к которым они относятся.

Область действия переменной является лексической:

fn main() {
    let string1 = String::from("long string is long"); // <-- scope of string1 begins here
    let result;
    {
        let string2 = String::from("xyz"); // <-- scope of string2 begins here
        result = longest(string1.as_str(), string2.as_str());
        // <-- scope of string2 ends here
    }
    println!("The longest string is {}", result);
    // <-- scope of string1 ends here
}

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

fn main() {
    let string1 = String::from("long string is long");
    let result;
    {
        let string2 = String::from("xyz");
        // The lifetime of result cannot be longer than `'a` 
        result = longest(string1.as_str(), string2.as_str());
        // But a reference to string2 also has lifetime `'a`, which means that
        // the lifetime `'a` is only valid for the scope of string2 
        // <-- i.e. to here
    }
    // But then we try to use it here — oops!
    println!("The longest string is {}", result);
}

Мы сказали Rust, что время жизниссылки, возвращаемой самой длинной функцией, совпадает с меньшим временем жизни ссылок, переданных в.

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

В вашем примере вы могли бы сделать функцию main действительнойизменив аннотации времени жизни на longest:

fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y // oops!
    }
}

Но теперь вы получаете ошибку внутри longest, поскольку она больше не отвечает требованиям: теперь она никогда не будет действительной для возвратаy потому что его время жизни может быть короче 'a.Фактически, единственные способы правильно реализовать эту функцию:

  1. Возврат x
  2. Возврат среза x
  3. Возврат &'static str- поскольку 'static переживает все другие времена жизни
  4. Используйте unsafe код
...