Lifetimes

03 ноември 2022

Административни неща

Преговор

Преговор

Преговор

Преговор

Преговор

Преговор

Lifetimes

Lifetimes

Ще говорим за интервала в който една стойност е жива

1 2 3 4 5 6 7
fn main()
{
    {
        let x = 5; // --+
                   //   |
    } // <--------------+
}
fn main()
{
    {
        let x = 5; // --+
                   //   |
    } // <--------------+
}

Lifetime на променлива със собственост

Стойността живее докато променливата е в scope

1 2 3 4 5
fn main()
{
    let s = String::from("five"); // --+
                                  //   |
} // <---------------------------------+
fn main()
{
    let s = String::from("five"); // --+
                                  //   |
} // <---------------------------------+

Lifetime на променлива със собственост

Стойността живее докато променливата е в scope

1 2 3 4 5 6 7 8 9
fn main()
{
    let s = String::from("five"); // --+
                                  //   |
                 // v------------------+
    takes_ownership(s);
}

fn takes_ownership(s: String) {}
fn main()
{
    let s = String::from("five"); // --+
                                  //   |
                 // v------------------+
    takes_ownership(s);
}

fn takes_ownership(s: String) {}

Lifetime на референция

Референцията живее до мястото където е използвана за последно

1 2 3 4 5 6 7 8
fn main()
{
    let x = 5;
    let r = &x; // ----+
                //     |
                // v---+
    println!("{}", r);
}
5
fn main()
{
    let x = 5;
    let r = &x; // ----+
                //     |
                // v---+
    println!("{}", r);
}

Lifetime на референция

Референцията също така не може да надживее стойността, към която сочи

1 2 3 4 5 6 7 8
fn main()
{
    let x = 5;  //--------+
    let r = &x; // ----+  |
                //     |  |
                // v---+  |
    println!("{}", r); // |
} // <--------------------+
5
fn main()
{
    let x = 5;  //--------+
    let r = &x; // ----+  |
                //     |  |
                // v---+  |
    println!("{}", r); // |
} // <--------------------+

Lifetime на референция

Иначе borrow checker-а ще се скара

1 2 3 4 5 6 7 8 9 10 11
fn main()
{
    let r;
    {
        let x = 5;  //----+
        r = &x; // ----+  |
                //     |  |
    } // <----------------+
                // v---+
    println!("{}", r);
}

Lifetime на референция

Иначе borrow checker-а ще се скара

1 2 3 4 5 6 7 8 9 10 11
fn main()
{
    let r;
    {
        let x = 5;  //----+
        r = &x; // ----+  |
                //     |  |
    } // <----------------+
                // v---+
    println!("{}", r);
}
error[E0597]: `x` does not live long enough --> src/bin/main_fa10d38da86d8e94726f89cb8f0b106ec7b8be47.rs:6:13 | 6 | r = &x; // ----+ | | ^^ borrowed value does not live long enough 7 | // | | 8 | } // <----------------+ | - `x` dropped here while still borrowed 9 | // v---+ 10 | println!("{}", r); | - borrow later used here For more information about this error, try `rustc --explain E0597`. error: could not compile `rust` due to previous error
fn main()
{
    let r;
    {
        let x = 5;  //----+
        r = &x; // ----+  |
                //     |  |
    } // <----------------+
                // v---+
    println!("{}", r);
}

Lifetimes

Нека пробваме с малко по-сложен пример…

1 2 3 4 5 6 7 8 9 10 11 12 13
fn main()
{
    let x = 5;

    let r2 = {
        let r1 = &x;


        &*r1
    };

    println!("{}", r2);
}
fn main()
{
    let x = 5;

    let r2 = {
        let r1 = &x;


        &*r1
    };

    println!("{}", r2);
}

Lifetimes

Нека пробваме с малко по-сложен пример… Работи

1 2 3 4 5 6 7 8 9 10 11 12 13
fn main()
{
    let x = 5;

    let r2 = {
        let r1 = &x;


        &*r1
    };

    println!("{}", r2);
}
5
fn main()
{
    let x = 5;

    let r2 = {
        let r1 = &x;


        &*r1
    };

    println!("{}", r2);
}

Lifetimes

Можем да означим колко дълго живеят x, r1 и r2

1 2 3 4 5 6 7 8 9 10 11 12
{
    let x = 5; //-----------+
               //           |
    let r2 = { //           |
        let r1 = &x; // --+ |
                     //   | |
        // v--------------+ |
        &*r1 // --------+   |
    };           //     |   |
                 // v---+   |
    println!("{}", r2); //  |
} // <----------------------+
5
fn main()
{
    let x = 5; //-----------+
               //           |
    let r2 = { //           |
        let r1 = &x; // --+ |
                     //   | |
        // v--------------+ |
        &*r1 // --------+   |
    };           //     |   |
                 // v---+   |
    println!("{}", r2); //  |
} // <----------------------+

Lifetimes

Можем да означим колко дълго живеят x, r1 и r2

1 2 3 4 5 6 7 8 9 10 11 12
{
    let x = 5; //-----------+
               //           |
    let r2 = { //           |
        let r1 = &x; // --+ |
                     //   | |
        // v--------------+ |
        &*r1 // --------+   |
    };           //     |   |
                 // v---+   |
    println!("{}", r2); //  |
} // <----------------------+
5
fn main()
{
    let x = 5; //-----------+
               //           |
    let r2 = { //           |
        let r1 = &x; // --+ |
                     //   | |
        // v--------------+ |
        &*r1 // --------+   |
    };           //     |   |
                 // v---+   |
    println!("{}", r2); //  |
} // <----------------------+

r2 е референция към стойността зад x. Важно е r2 да не надживява x

Lifetimes

Lifetimes

1 2 3 4
/// Връща по-дългия от двата низа
fn longer(s1: &str, s2: &str) -> &str {
    if s1.len() > s2.len() { s1 } else { s2 }
}

Lifetimes

Как би работило в следния случай?

1 2 3 4 5
let s1 = String::from("looong");
let l = {
    let s2 = String::from("123");
    longer(&s1[..], &s2[..])
};
fn longer(_: &str, _: &str) {}
fn main() {
let s1 = String::from("looong");
let l = {
    let s2 = String::from("123");
    longer(&s1[..], &s2[..])
};
}

Lifetimes

Как би работило в следния случай?

1 2 3 4 5
let s1 = { let mut s = String::new(); std::io::stdin().read_line(&mut s); s };
let l = {
    let s2 = { let mut s = String::new(); std::io::stdin().read_line(&mut s); s };
    longer(&s1[..], &s2[..])
};
#![allow(unused_must_use)]
fn longer(_: &str, _: &str) {}
fn main() {
let s1 = { let mut s = String::new(); std::io::stdin().read_line(&mut s); s };
let l = {
    let s2 = { let mut s = String::new(); std::io::stdin().read_line(&mut s); s };
    longer(&s1[..], &s2[..])
};
}

Lifetimes

Lifetimes

1 2 3 4
/// Връща по-дългия от двата низа
fn longer(s1: &str, s2: &str) -> &str {
    if s1.len() > s2.len() { s1 } else { s2 }
}
error[E0106]: missing lifetime specifier --> src/bin/main_33b4ac338e5075f0ff9097f3f20bad54061384df.rs:4:34 | 4 | fn longer(s1: &str, s2: &str) -> &str { | ---- ---- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `s1` or `s2` help: consider introducing a named lifetime parameter | 4 | fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str { | ++++ ++ ++ ++ For more information about this error, try `rustc --explain E0106`. error: could not compile `rust` due to previous error
fn main() {}
/// Връща по-дългия от двата низа
fn longer(s1: &str, s2: &str) -> &str {
    if s1.len() > s2.len() { s1 } else { s2 }
}

Lifetime анотации

Lifetime анотации

Какъв е проблема?

Lifetime анотации

Какъв е проблема?

Lifetime анотации

Какъв е проблема?

Lifetime анотации

Какъв е проблема?

Lifetime анотации

Какъв е проблема?

Lifetime анотации

Грешката ни казва, че трябва да означим дали върнатият резултат живее колкото параметъра s1 или колкото параметъра s2

1 2 3 4
/// Връща по-дългия от двата низа
fn longer(s1: &str, s2: &str) -> &str {
    if s1.len() > s2.len() { s1 } else { s2 }
}
error[E0106]: missing lifetime specifier --> src/bin/main_33b4ac338e5075f0ff9097f3f20bad54061384df.rs:4:34 | 4 | fn longer(s1: &str, s2: &str) -> &str { | ---- ---- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `s1` or `s2` help: consider introducing a named lifetime parameter | 4 | fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str { | ++++ ++ ++ ++ For more information about this error, try `rustc --explain E0106`. error: could not compile `rust` due to previous error
fn main() {}
/// Връща по-дългия от двата низа
fn longer(s1: &str, s2: &str) -> &str {
    if s1.len() > s2.len() { s1 } else { s2 }
}

Lifetime анотации

Отговорът: и трите живеят еднакво дълго

1 2 3 4
/// Връща по-дългия от двата низа
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str {
    if s1.len() > s2.len() { s1 } else { s2 }
}
fn main() {}
/// Връща по-дългия от двата низа
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str {
    if s1.len() > s2.len() { s1 } else { s2 }
}

'a

'a

'a

'a

'a

'a

'a

'a

1
fn foo<'a>(x: &'a str) { }
fn foo<'a>(x: &'a str) { }
fn main() {}

'a

1
fn foo<'a>(x: &'a str) { }
fn foo<'a>(x: &'a str) { }
fn main() {}

'a

1
fn foo<'a>(x: &'a str, y: &'a str) { }
fn foo<'a>(x: &'a str, y: &'a str) { }
fn main() {}

'a

1
fn foo<'a>(x: &'a str, y: &'a str) { }
fn foo<'a>(x: &'a str, y: &'a str) { }
fn main() {}

'a

1
fn foo<'a>(x: &'a str, y: &'a str) { }
fn foo<'a>(x: &'a str, y: &'a str) { }
fn main() {}

'a

1
fn trim<'a>(s: &'a str) -> &'a str { }

'a

1
fn trim<'a>(s: &'a str) -> &'a str { }

Lifetimes - примери

Функцията trim връща резултат който живее колкото аргумента

1 2 3 4 5 6 7 8 9
fn trim<'a>(s: &'a str) -> &'a str

fn main() {
    {
        let s = String::from("  низ  \n"); // --+
        let trimmed = trim(&s);            // --|-+
                                           //   | |
    } // <--------------------------------------+-+
}
fn trim<'a>(s: &'a str) -> &'a str
{ s.trim() }

fn main() {
    {
        let s = String::from("  низ  \n"); // --+
        let trimmed = trim(&s);            // --|-+
                                           //   | |
    } // <--------------------------------------+-+
}

Lifetimes - примери

Функцията longer връща резултат който живее колкото общия период в който живеят двата ѝ аргумента.

1 2 3 4 5 6 7 8 9
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str

fn main() {
    let s1 = String::from("дългият низ е дълъг");
    {
        let s2 = String::from("къс низ");
        let result = longer(&s1, &s2);
    }
}
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str
{ unimplemented!() }

fn main() {
    let s1 = String::from("дългият низ е дълъг");
    {
        let s2 = String::from("къс низ");
        let result = longer(&s1, &s2);
    }
}

Lifetimes - примери

Ако двата аргумента живеят различно - ще се вземе по-малкия период

1 2 3 4 5 6 7 8 9 10
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str

fn main() {
    let s1 = String::from("дългият низ е дълъг"); // --+
    {                                             //   |
        let s2 = String::from("къс низ"); // --+       |
        let result = longer(&s1, &s2);    // --|--+    |
                                          //   |  |    |
    } // <-------------------------------------+--+    |
} // <-------------------------------------------------+
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str
{ unimplemented!() }

fn main() {
    let s1 = String::from("дългият низ е дълъг"); // --+
    {                                             //   |
        let s2 = String::from("къс низ"); // --+       |
        let result = longer(&s1, &s2);    // --|--+    |
                                          //   |  |    |
    } // <-------------------------------------+--+    |
} // <-------------------------------------------------+

Lifetimes - примери

Ако двата аргумента живеят различно - ще се вземе по-малкия период

1 2 3 4 5 6 7 8 9 10
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str

fn main() {
    let s1 = String::from("дългият низ е дълъг"); // --+
    {                                             //   |
        let s2 = String::from("къс низ"); // --+       |
        let result = longer(&s1, &s2);    // --|--+    |
                                          //   |  |    |
    } // <-------------------------------------+--+    |
} // <-------------------------------------------------+
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str
{ unimplemented!() }

fn main() {
    let s1 = String::from("дългият низ е дълъг"); // --+
    {                                             //   |
        let s2 = String::from("къс низ"); // --+       |
        let result = longer(&s1, &s2);    // --|--+    |
                                          //   |  |    |
    } // <-------------------------------------+--+    |
} // <-------------------------------------------------+

Lifetimes - примери

Не е нужно всички lifetime-и да са еднакви

1 2 3 4 5 6 7 8 9 10 11 12 13 14
// резултатът е свързан с оригиналния низ `s`,  но не е свързан с `pattern`
fn first_occurrence<'a, 'b>(s: &'a str, pattern: &'b str) -> Option<&'a str> {
    s.matches(pattern).next()
}

fn main() {
    let text = String::from("обичам мач и боза");
    let result = {
        let pattern = String::from("боза");
        first_occurrence(&text, &pattern)
    };

    println!("{:?}", result);
}
Some("боза")
fn first_occurrence<'a, 'b>(s: &'a str, pattern: &'b str) -> Option<&'a str> {
    s.matches(pattern).next()
}

fn main() {
    let text = String::from("обичам мач и боза");
    let result = {
        let pattern = String::from("боза");
        first_occurrence(&text, &pattern)
    };

    println!("{:?}", result);
}

Lifetime elision

Lifetime elision

Lifetime elision

Lifetime elision

Lifetime elision

Lifetime elision

Следните дефиниции са еквивалентни:

1 2 3
fn trim(s: &str) -> &str {
    // ...
}
fn trim(s: &str) -> &str {
    // ...
unimplemented!()
}
fn main() {}
1 2 3
fn trim<'a>(s: &'a str) -> &'a str {
    // ...
}
fn trim<'a>(s: &'a str) -> &'a str {
    // ...
unimplemented!()
}
fn main() {}

Lifetime elision

Кога трябва да пишем lifetimes?

Lifetime elision

Кога трябва да пишем lifetimes?

Lifetime elision

Кога трябва да пишем lifetimes?

Lifetime elision

Кога трябва да пишем lifetimes?

Lifetime elision

Как работи

За всеки пропуснат lifetime в аргументите се добавя нов lifetime параметър

1 2 3 4 5
fn print(s: &str);                                  // elided
fn print<'a>(s: &'a str);                           // expanded

fn foo(x: (&u32, &u32), y: usize);                  // elided
fn foo<'a, 'b>(x: (&'a u32, &'b u32), y: usize);    // expanded

Lifetime elision

Как работи

(1) Ако за аргументите има само един lifetime параметър (експлицитен или пропуснат), този lifetime се налага на всички пропуснати lifetimes в резултата

1 2 3 4 5
fn substr(s: &str, until: usize) -> &str;                         // elided
fn substr<'a>(s: &'a str, until: usize) -> &'a str;               // expanded

fn split_at(s: &str, pos: usize) -> (&str, &str);                 // elided
fn split_at<'a>(s: &'a str, pos: usize) -> (&'a str, &'a str);    // expanded

Lifetime elision

Как работи

(2) Ако първият аргумент е &self или &mut self, неговия lifetime се налага на всички пропуснати lifetimes в резултата

1 2 3 4 5
fn get_mut(&mut self) -> &mut T;                                // elided
fn get_mut<'a>(&'a mut self) -> &'a mut T;                      // expanded

fn args(&mut self, args: &[T]) -> &mut Self;                    // elided
fn args<'a, 'b>(&'a mut self, args: &'b [T]) -> &'a mut Self;   // expanded

Lifetime elision

Как работи

(3) Във всички останали случаи е грешка да не напишем lifetime анотацията.

1 2 3 4 5 6 7
fn get_str() -> &str {
    // ...
}

fn longest(x: &str, y: &str) -> &str {
    // ...
}
error[E0106]: missing lifetime specifier --> src/bin/main_4c57f10ba9573eb28198bf67d30d12e7c2c70820.rs:1:17 | 1 | fn get_str() -> &str { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime | 1 | fn get_str() -> &'static str { | +++++++ error[E0106]: missing lifetime specifier --> src/bin/main_4c57f10ba9573eb28198bf67d30d12e7c2c70820.rs:6:33 | 6 | fn longest(x: &str, y: &str) -> &str { | ---- ---- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y` help: consider introducing a named lifetime parameter | 6 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { | ++++ ++ ++ ++ For more information about this error, try `rustc --explain E0106`. error: could not compile `rust` due to 2 previous errors
fn get_str() -> &str {
    // ...
unimplemented!()
}

fn longest(x: &str, y: &str) -> &str {
    // ...
unimplemented!()
}
fn main() {}

Lifetime elision

Примери

1 2 3 4 5 6 7 8 9
// няма нужда от анотация - ситуацията е еднозначна
fn trim(s: &str) -> &str { todo!() }

// трябва да означим, че резултата е свързан и с двата аргумента
fn longest<'a>(left: &'a str, right: &'a str) -> &'a str { todo!() }

// трябва да означим, че резултата е свързан само с първия аргумент
// не е нужно да слагаме lifetime анотация на втория
fn first_occurence<'a>(text: &'a str, pattern: &str) -> &'a str { todo!() }
fn trim(s: &str) -> &str { todo!() }

// трябва да означим, че резултата е свързан и с двата аргумента
fn longest<'a>(left: &'a str, right: &'a str) -> &'a str { todo!() }

// трябва да означим, че резултата е свързан само с първия аргумент
// не е нужно да слагаме lifetime анотация на втория
fn first_occurence<'a>(text: &'a str, pattern: &str) -> &'a str { todo!() }

fn main() {}

Lifetimes

Обобщение

Lifetimes

Обобщение

Lifetimes

Обобщение

Lifetimes

Обобщение

Статичен живот

Static Life

Статичен живот

Специалният lifetime 'static.

Оказва че променливата живее за целия живот на програмата.

1
let s: &'static str = "I have a static lifetime.";
fn main() {
let s: &'static str = "I have a static lifetime.";
}

Референции в структури

Референции в структури

Нека имаме структура, която връща думите от текст.

1 2 3 4 5 6 7 8 9 10 11 12 13
struct Words {
    text: ??,
}

impl Words {
    fn new(text: &str) -> Words {
        todo!()
    }

    fn next_word(&mut self) -> Option<&str> {
        todo!()
    }
}

Референции в структури

Нека имаме структура, която връща думите от текст.

1 2 3 4 5 6 7 8 9 10 11 12 13
struct Words {
    text: ??,
}

impl Words {
    fn new(text: &str) -> Words {
        todo!()
    }

    fn next_word(&mut self) -> Option<&str> {
        todo!()
    }
}

Референции в структури

Нека имаме структура, която връща думите от текст.

1 2 3 4 5 6 7 8 9 10 11 12 13
struct Words {
    text: ??,
}

impl Words {
    fn new(text: &str) -> Words {
        todo!()
    }

    fn next_word(&mut self) -> Option<&str> {
        todo!()
    }
}

Референции в структури

Нека имаме структура, която връща думите от текст.

1 2 3 4 5 6 7 8 9 10 11 12 13
struct Words {
    text: ??,
}

impl Words {
    fn new(text: &str) -> Words {
        todo!()
    }

    fn next_word(&mut self) -> Option<&str> {
        todo!()
    }
}

Референции в структури

1 2 3
struct Words {
    text: &str,
}
error[E0106]: missing lifetime specifier --> src/bin/main_afa8baa99e3366a4216f568a70ccdbcb9c7c3207.rs:2:11 | 2 | text: &str, | ^ expected named lifetime parameter | help: consider introducing a named lifetime parameter | 1 ~ struct Words<'a> { 2 ~ text: &'a str, | For more information about this error, try `rustc --explain E0106`. error: could not compile `rust` due to previous error
struct Words {
    text: &str,
}
fn main() {}

Референции в структури

1 2 3
struct Words<'a> {
    text: &'a str,
}
struct Words<'a> {
    text: &'a str,
}
fn main() {}

Референции в структури

Съответно трябва да добавим lifetime параметъра и към impl блока

1 2 3 4 5 6 7 8 9 10
#[derive(Debug)]
struct Words<'a> {
    text: &'a str,
}

impl<'a> Words<'a> {
    fn new(text: &str) -> Words {
        Words { text }
    }
}
#[derive(Debug)]
struct Words<'a> {
    text: &'a str,
}

impl<'a> Words<'a> {
    fn new(text: &str) -> Words {
        Words { text }
    }
}
fn main() {}

Референции в структури

Животът на структурата е ограничен до това колко живее обектът, от който сме взели референция

1 2 3 4 5 6
let t1 = Words::new("a b c");                // Words<'static>

{
    let s = String::from("мой таен низ");   // ---+- 'a
    Words::new(s.as_str());                 //    |- Words<'a>
}; // <-------------------------------------------+
#[derive(Debug)]
struct Words<'a> {
text: &'a str,
}
impl<'a> Words<'a> {
fn new(text: &str) -> Words {
Words { text }
}
}
fn main() {
let t1 = Words::new("a b c");                // Words<'static>

{
    let s = String::from("мой таен низ");   // ---+- 'a
    Words::new(s.as_str());                 //    |- Words<'a>
}; // <-------------------------------------------+
}

Lifetime elision в impl блок

Как се попълват пропуснатите lifetimes за функцията new?

1 2 3 4 5
impl<'a> Words<'a> {
    fn new(text: &str) -> Words {
        Words { text }
    }
}
struct Words<'a> {
text: &'a str,
}
impl<'a> Words<'a> {
    fn new(text: &str) -> Words {
        Words { text }
    }
}
fn main() {}

Lifetime elision в impl блок

Expanded:

1 2 3 4 5
impl<'a> Words<'a> {
    fn new<'b>(text: &'b str) -> Words<'b> {
        Words { text }
    }
}
struct Words<'a> {
text: &'a str,
}
impl<'a> Words<'a> {
    fn new<'b>(text: &'b str) -> Words<'b> {
        Words { text }
    }
}
fn main() {}

Lifetime elision в impl блок

Expanded:

1 2 3 4 5
impl<'a> Words<'a> {
    fn new<'b>(text: &'b str) -> Words<'b> {
        Words { text }
    }
}
struct Words<'a> {
text: &'a str,
}
impl<'a> Words<'a> {
    fn new<'b>(text: &'b str) -> Words<'b> {
        Words { text }
    }
}
fn main() {}

Lifetime elision в impl блок

Expanded:

1 2 3 4 5
impl<'a> Words<'a> {
    fn new<'b>(text: &'b str) -> Words<'b> {
        Words { text }
    }
}
struct Words<'a> {
text: &'a str,
}
impl<'a> Words<'a> {
    fn new<'b>(text: &'b str) -> Words<'b> {
        Words { text }
    }
}
fn main() {}

Lifetime elision в impl блок

Ами ако използваме Self?

1 2 3 4 5
impl<'a> Words<'a> {
    fn new(text: &str) -> Self {
        Words { text }
    }
}

Lifetime elision в impl блок

Ами ако използваме Self?

1 2 3 4 5
impl<'a> Words<'a> {
    fn new(text: &str) -> Self {
        Words { text }
    }
}
error[E0621]: explicit lifetime required in the type of `text` --> src/bin/main_20500598e5171c82492ec4a5e7146083406968cd.rs:6:9 | 5 | fn new(text: &str) -> Self { | ---- help: add explicit lifetime `'a` to the type of `text`: `&'a str` 6 | Words { text } | ^^^^^^^^^^^^^^ lifetime `'a` required For more information about this error, try `rustc --explain E0621`. error: could not compile `rust` due to previous error
struct Words<'a> {
text: &'a str,
}
impl<'a> Words<'a> {
    fn new(text: &str) -> Self {
        Words { text }
    }
}
fn main() {}

Lifetime elision в impl блок

В случая Self означава Words<'a>:

1 2 3 4 5
impl<'a> Words<'a> {
    fn new(text: &str) -> Words<'a> {
        Words { text }
    }
}
error[E0621]: explicit lifetime required in the type of `text` --> src/bin/main_392932f4ba7aa505606ed5e92a3e6a8c158cca2a.rs:6:9 | 5 | fn new(text: &str) -> Words<'a> { | ---- help: add explicit lifetime `'a` to the type of `text`: `&'a str` 6 | Words { text } | ^^^^^^^^^^^^^^ lifetime `'a` required For more information about this error, try `rustc --explain E0621`. error: could not compile `rust` due to previous error
struct Words<'a> {
text: &'a str,
}
impl<'a> Words<'a> {
    fn new(text: &str) -> Words<'a> {
        Words { text }
    }
}
fn main() {}

Lifetime elision в impl блок

Expanded:

1 2 3 4 5
impl<'a> Words<'a> {
    fn new<'b>(text: &'b str) -> Words<'a> {
        Words { text }
    }
}
error: lifetime may not live long enough --> src/bin/main_154710e0a8f26410179dbda3cef5acb4d7942703.rs:6:9 | 4 | impl<'a> Words<'a> { | -- lifetime `'a` defined here 5 | fn new<'b>(text: &'b str) -> Words<'a> { | -- lifetime `'b` defined here 6 | Words { text } | ^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` | = help: consider adding the following bound: `'b: 'a` error: could not compile `rust` due to previous error
struct Words<'a> {
text: &'a str,
}
impl<'a> Words<'a> {
    fn new<'b>(text: &'b str) -> Words<'a> {
        Words { text }
    }
}
fn main() {}

Lifetime elision в impl блок

Ако искаме да използваме Self, правилния вариант е:

1 2 3 4 5
impl<'a> Words<'a> {
    fn new(text: &'a str) -> Self {
        Words { text }
    }
}
struct Words<'a> {
text: &'a str,
}
impl<'a> Words<'a> {
    fn new(text: &'a str) -> Self {
        Words { text }
    }
}
fn main() {}

Имплементация на next_word метода

1 2 3 4 5 6 7 8 9 10 11 12 13 14
#[derive(Debug)]
struct Words<'a> {
    text: &'a str,
}

impl<'a> Words<'a> {
    fn new(text: &'a str) -> Self {
        Words { text }
    }

    fn next_word(&mut self) -> Option<&str> {
        todo!()
    }
}
#[derive(Debug)]
struct Words<'a> {
    text: &'a str,
}

impl<'a> Words<'a> {
    fn new(text: &'a str) -> Self {
        Words { text }
    }

    fn next_word(&mut self) -> Option<&str> {
        todo!()
    }
}
fn main() {}

Имплементация на next_word метода

Вариант със str::split_once

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
#[derive(Debug)]
struct Words<'a> {
    text: Option<&'a str>,
}

impl<'a> Words<'a> {
    fn next_word(&mut self) -> Option<&str> {
        let text = self.text?;

        match text.split_once(char::is_whitespace) {
            Some((word, rest)) => {
                self.text = Some(rest);
                Some(word)
            },
            None => {
                self.text = None;
                Some(text)
            }
        }
    }
}
#[derive(Debug)]
struct Words<'a> {
    text: Option<&'a str>,
}

impl<'a> Words<'a> {
    fn next_word(&mut self) -> Option<&str> {
        let text = self.text?;

        match text.split_once(char::is_whitespace) {
            Some((word, rest)) => {
                self.text = Some(rest);
                Some(word)
            },
            None => {
                self.text = None;
                Some(text)
            }
        }
    }
}
fn main() {}

Имплементация на next_word метода

Вариант със str::splitn

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#[derive(Debug)]
struct Words<'a> {
    text: Option<&'a str>,
}

impl<'a> Words<'a> {
    fn next_word(&mut self) -> Option<&str> {
        let text = self.text?;
        let mut iter = text.splitn(2, char::is_whitespace);

        match (iter.next(), iter.next()) {
            (Some(word), rest) => {
                self.text = rest;
                Some(word)
            },
            _ => unreachable!()
        }
    }
}
#[derive(Debug)]
struct Words<'a> {
    text: Option<&'a str>,
}

impl<'a> Words<'a> {
    fn next_word(&mut self) -> Option<&str> {
        let text = self.text?;
        let mut iter = text.splitn(2, char::is_whitespace);

        match (iter.next(), iter.next()) {
            (Some(word), rest) => {
                self.text = rest;
                Some(word)
            },
            _ => unreachable!()
        }
    }
}
fn main() {}

Имплементация на next_word метода

Тестване

1 2 3 4 5 6
fn main() {
    let mut words = Words::new("hello world");
    println!("{:?}", words.next_word());
    println!("{:?}", words.next_word());
    println!("{:?}", words.next_word());
}
Some("hello") Some("world") None
#[derive(Debug)]
struct Words<'a> {
text: Option<&'a str>,
}

impl<'a> Words<'a> {
fn new(text: &'a str) -> Self {
Words { text: Some(text) }
}
fn next_word(&mut self) -> Option<&str> {
let text = self.text?;

match text.split_once(char::is_whitespace) {
Some((word, rest)) => {
self.text = Some(rest);
Some(word)
},
None => {
self.text = None;
Some(text)
}
}
}
}
fn main() {
    let mut words = Words::new("hello world");
    println!("{:?}", words.next_word());
    println!("{:?}", words.next_word());
    println!("{:?}", words.next_word());
}

Имплементация на next_word метода

А може и с unit test

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
#[cfg(test)]
mod tests{
    use super::*;

    #[test]
    fn words_basic() {
        let mut words = Words::new("hello world");
        assert_eq!(words.next_word(), Some("hello"));
        assert_eq!(words.next_word(), Some("world"));
        assert_eq!(words.next_word(), None);
    }

    #[test]
    fn empty_string() {
        let mut words = Words::new("");
        assert_eq!(words.next_word(), Some(""));
        assert_eq!(words.next_word(), None);
    }

    #[test]
    fn trailing_whitespace() {
        let mut words = Words::new("hello world ");
        assert_eq!(words.next_word(), Some("hello"));
        assert_eq!(words.next_word(), Some("world"));
        assert_eq!(words.next_word(), Some(""));
        assert_eq!(words.next_word(), None);
    }
}
#[derive(Debug)]
struct Words<'a> {
text: Option<&'a str>,
}

impl<'a> Words<'a> {
fn next_word(&mut self) -> Option<&str> {
let text = self.text?;
let mut iter = text.splitn(2, char::is_whitespace);

match (iter.next(), iter.next()) {
(Some(word), rest) => {
self.text = rest;
Some(word)
},
_ => unreachable!()
}
}
}
#[cfg(test)]
mod tests{
    use super::*;

    #[test]
    fn words_basic() {
        let mut words = Words::new("hello world");
        assert_eq!(words.next_word(), Some("hello"));
        assert_eq!(words.next_word(), Some("world"));
        assert_eq!(words.next_word(), None);
    }

    #[test]
    fn empty_string() {
        let mut words = Words::new("");
        assert_eq!(words.next_word(), Some(""));
        assert_eq!(words.next_word(), None);
    }

    #[test]
    fn trailing_whitespace() {
        let mut words = Words::new("hello world ");
        assert_eq!(words.next_word(), Some("hello"));
        assert_eq!(words.next_word(), Some("world"));
        assert_eq!(words.next_word(), Some(""));
        assert_eq!(words.next_word(), None);
    }
}
fn main() {}

Имплементация на next_word метода

Lifetimes

Всичко работи, но дали имаме правилните lifetimes?

1 2 3 4
fn hello() -> &'static str {
    let mut words = Words::new("hello world");
    words.next_word().unwrap()
}

Имплементация на next_word метода

Lifetimes

Всичко работи, но дали имаме правилните lifetimes?

1 2 3 4
fn hello() -> &'static str {
    let mut words = Words::new("hello world");
    words.next_word().unwrap()
}
error[E0515]: cannot return value referencing local variable `words` --> src/bin/main_1aa46f44383ccf6f88e4cd137658789c5d4d2beb.rs:25:5 | 25 | words.next_word().unwrap() | -----------------^^^^^^^^^ | | | returns a value referencing data owned by the current function | `words` is borrowed here For more information about this error, try `rustc --explain E0515`. error: could not compile `rust` due to previous error
#[derive(Debug)]
struct Words<'a> {
text: Option<&'a str>,
}
impl<'a> Words<'a> {
fn new(text: &'a str) -> Self {
Words { text: Some(text) }
}
fn next_word(&mut self) -> Option<&str> {
let text = self.text?;
match text.split_once(char::is_whitespace) {
Some((word, rest)) => {
self.text = Some(rest);
Some(word)
},
None => {
self.text = None;
Some(text)
}
}
}
}
fn hello() -> &'static str {
    let mut words = Words::new("hello world");
    words.next_word().unwrap()
}
fn main() {}

Имплементация на next_word метода

Lifetimes

Това става, защото резултата от next_word има lifetime колкото self ('b).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
impl<'a> Words<'a> {
    fn next_word<'b>(&'b mut self) -> Option<&'b str> {
        let text = self.text?;

        match text.split_once(char::is_whitespace) {
            Some((word, rest)) => {
                self.text = Some(rest);
                Some(word)
            },
            None => {
                self.text = None;
                Some(text)
            }
        }
    }
}
#[derive(Debug)]
struct Words<'a> {
text: Option<&'a str>,
}
impl<'a> Words<'a> {
    fn next_word<'b>(&'b mut self) -> Option<&'b str> {
        let text = self.text?;

        match text.split_once(char::is_whitespace) {
            Some((word, rest)) => {
                self.text = Some(rest);
                Some(word)
            },
            None => {
                self.text = None;
                Some(text)
            }
        }
    }
}
fn main() {}

Имплементация на next_word метода

Lifetimes

Вместо това може да върнем резултат с lifetime-а на оригиналния низ в полето text ('a).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
impl<'a> Words<'a> {
    fn next_word(&mut self) -> Option<&'a str> {
        // използваме lifetime 'a тук  ^^

        let text = self.text?;

        match text.split_once(char::is_whitespace) {
            Some((word, rest)) => {
                self.text = Some(rest);
                Some(word)
            },
            None => {
                self.text = None;
                Some(text)
            }
        }
    }
}

fn hello() -> &'static str {
    let mut words = Words::new("hello world");
    words.next_word().unwrap()
}

fn main() {
    println!("{}", hello());
}
hello
#[derive(Debug)]
struct Words<'a> {
text: Option<&'a str>,
}
impl<'a> Words<'a> {
fn new(text: &'a str) -> Self {
Words { text: Some(text) }
}
}
impl<'a> Words<'a> {
    fn next_word(&mut self) -> Option<&'a str> {
        // използваме lifetime 'a тук  ^^

        let text = self.text?;

        match text.split_once(char::is_whitespace) {
            Some((word, rest)) => {
                self.text = Some(rest);
                Some(word)
            },
            None => {
                self.text = None;
                Some(text)
            }
        }
    }
}

fn hello() -> &'static str {
    let mut words = Words::new("hello world");
    words.next_word().unwrap()
}

fn main() {
    println!("{}", hello());
}

Имплементация на next_word метода

Финален код

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
#[derive(Debug)]
struct Words<'a> {
    text: Option<&'a str>,
}

impl<'a> Words<'a> {
    fn new(text: &'a str) -> Self {
        Words { text: Some(text) }
    }

    fn next_word(&mut self) -> Option<&'a str> {
        let text = self.text?;

        match text.split_once(char::is_whitespace) {
            Some((word, rest)) => {
                self.text = Some(rest);
                Some(word)
            },
            None => {
                self.text = None;
                Some(text)
            }
        }
    }
}
#[derive(Debug)]
struct Words<'a> {
    text: Option<&'a str>,
}

impl<'a> Words<'a> {
    fn new(text: &'a str) -> Self {
        Words { text: Some(text) }
    }

    fn next_word(&mut self) -> Option<&'a str> {
        let text = self.text?;

        match text.split_once(char::is_whitespace) {
            Some((word, rest)) => {
                self.text = Some(rest);
                Some(word)
            },
            None => {
                self.text = None;
                Some(text)
            }
        }
    }
}

fn main() {}

Lifetimes & generics

Lifetimes & generics

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
trait MyTrait {}
impl MyTrait for String {}

struct Wrapper<T: MyTrait>(T);

fn save_for_later<T: MyTrait>(something: T) -> Wrapper<T> {
    Wrapper(something)
}

fn main() {
    let saved = {
        let s = String::from("yippie");
        save_for_later(s)
    };

    let inner = &saved.0;
    println!("{}", inner);
}
yippie
trait MyTrait {}
impl MyTrait for String {}

struct Wrapper(T);

fn save_for_later(something: T) -> Wrapper {
    Wrapper(something)
}

fn main() {
    let saved = {
        let s = String::from("yippie");
        save_for_later(s)
    };

    let inner = &saved.0;
    println!("{}", inner);
}

Lifetimes & generics

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
trait MyTrait {}
impl<'a> MyTrait for &'a String {}

struct Wrapper<T: MyTrait>(T);

fn save_for_later<T: MyTrait>(something: T) -> Wrapper<T> {
    Wrapper(something)
}

fn main() {
    let saved = {
        let s = String::from("yippie");
        save_for_later(&s)
    };

    let inner = &saved.0;
    println!("{}", inner);
}
error[E0597]: `s` does not live long enough --> src/bin/main_944046f574413164dd1bf391cfeecc1e2abeb8d7.rs:13:24 | 11 | let saved = { | ----- borrow later stored here 12 | let s = String::from("yippie"); 13 | save_for_later(&s) | ^^ borrowed value does not live long enough 14 | }; | - `s` dropped here while still borrowed For more information about this error, try `rustc --explain E0597`. error: could not compile `rust` due to previous error
trait MyTrait {}
impl<'a> MyTrait for &'a String {}

struct Wrapper(T);

fn save_for_later(something: T) -> Wrapper {
    Wrapper(something)
}

fn main() {
    let saved = {
        let s = String::from("yippie");
        save_for_later(&s)
    };

    let inner = &saved.0;
    println!("{}", inner);
}

Lifetimes & generics

Lifetimes & generics

Lifetimes & generics

Lifetimes & generics

Lifetimes & generics

Lifetimes & generics

Aко искаме да запазим нещо за дълго можем да използваме ограничение 'static

1 2 3 4 5 6 7 8 9 10
fn save_for_later<T: MyTrait + 'static>(something: T) -> Wrapper<T> {
    Wrapper(something)
}

fn main() {
    let saved = {
        let s = String::from("yippie");
        save_for_later(s)   // OK, T = String => T: 'static
    };
}
trait MyTrait {}
impl MyTrait for String {}
impl<'a> MyTrait for &'a String {}
struct Wrapper(T);

fn save_for_later(something: T) -> Wrapper {
    Wrapper(something)
}

fn main() {
    let saved = {
        let s = String::from("yippie");
        save_for_later(s)   // OK, T = String => T: 'static
    };
}

Lifetimes & generics

Aко искаме да запазим нещо за дълго можем да използваме ограничение 'static

1 2 3 4 5 6 7 8 9 10
fn save_for_later<T: MyTrait + 'static>(something: T) -> Wrapper<T> {
    Wrapper(something)
}

fn main() {
    let saved = {
        let s = String::from("yippie");
        save_for_later(&s)   // Err, T = &'a String => T: 'a
    };
}
error[E0597]: `s` does not live long enough --> src/bin/main_47863cb4318a5526d1168585843d28102ada988b.rs:13:24 | 13 | save_for_later(&s) // Err, T = &'a String => T: 'a | ---------------^^- | | | | | borrowed value does not live long enough | argument requires that `s` is borrowed for `'static` 14 | }; | - `s` dropped here while still borrowed For more information about this error, try `rustc --explain E0597`. error: could not compile `rust` due to previous error
trait MyTrait {}
impl MyTrait for String {}
impl<'a> MyTrait for &'a String {}
struct Wrapper(T);

fn save_for_later(something: T) -> Wrapper {
    Wrapper(something)
}

fn main() {
    let saved = {
        let s = String::from("yippie");
        save_for_later(&s)   // Err, T = &'a String => T: 'a
    };
}

Lifetimes, generics & dynamic dispatch

Имаме подобен проблем ако искаме да ползваме dynamic dispatch

1 2 3 4 5 6 7
fn save_for_later<T: MyTrait>(something: T) -> Box<dyn MyTrait> {
    Box::new(something)
}

fn main() {
    // ...
}
error[E0310]: the parameter type `T` may not live long enough --> src/bin/main_b51c04a1a4b92edadf0fafcba2db31fdf9574870.rs:6:5 | 6 | Box::new(something) | ^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds | help: consider adding an explicit lifetime bound... | 5 | fn save_for_later<T: MyTrait + 'static>(something: T) -> Box<dyn MyTrait> { | +++++++++ For more information about this error, try `rustc --explain E0310`. error: could not compile `rust` due to previous error
trait MyTrait {}
impl MyTrait for String {}
impl<'a> MyTrait for &'a String {}

fn save_for_later(something: T) -> Box {
    Box::new(something)
}

fn main() {
    // ...
}

Lifetimes, generics & dynamic dispatch

По подразбиране dyn Trait има ограничение 'static

1 2 3 4 5 6 7
fn save_for_later<T: MyTrait>(something: T) -> Box<dyn MyTrait + 'static> {
    Box::new(something)
}

fn main() {
    // ...
}
error[E0310]: the parameter type `T` may not live long enough --> src/bin/main_0c50acb87194fec8e19b1308b6c107246b96e4f2.rs:6:5 | 6 | Box::new(something) | ^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds | help: consider adding an explicit lifetime bound... | 5 | fn save_for_later<T: MyTrait + 'static>(something: T) -> Box<dyn MyTrait + 'static> { | +++++++++ For more information about this error, try `rustc --explain E0310`. error: could not compile `rust` due to previous error
trait MyTrait {}
impl MyTrait for String {}
impl<'a> MyTrait for &'a String {}

fn save_for_later(something: T) -> Box {
    Box::new(something)
}

fn main() {
    // ...
}

Lifetimes, generics & dynamic dispatch

Можем да ограничим функцията да работи само със "static" типове

1 2 3 4 5 6 7
fn save_for_later<T: MyTrait + 'static>(something: T) -> Box<dyn MyTrait> {
    Box::new(something)
}

fn main() {
    // ...
}
trait MyTrait {}
impl MyTrait for String {}
impl<'a> MyTrait for &'a String {}

fn save_for_later(something: T) -> Box {
    Box::new(something)
}

fn main() {
    // ...
}

Lifetimes, generics & dynamic dispatch

Можем да сложим и различно ограничение

1 2 3 4 5 6 7 8
fn save_for_later<'a, T: MyTrait + 'a>(something: T) -> Box<dyn MyTrait + 'a> {
    Box::new(something)
}

fn main() {
    let s = String::from("yippie");
    let saved = save_for_later(&s);
}
trait MyTrait {}
impl MyTrait for String {}
impl<'a> MyTrait for &'a String {}

fn save_for_later<'a, T: MyTrait + 'a>(something: T) -> Box {
    Box::new(something)
}

fn main() {
    let s = String::from("yippie");
    let saved = save_for_later(&s);
}

Lifetimes, generics & dynamic dispatch

Разбира се, в този случай резултатът от функцията има ограничен lifetime

1 2 3 4 5 6 7 8 9 10
fn save_for_later<'a, T: MyTrait + 'a>(something: T) -> Box<dyn MyTrait + 'a> {
    Box::new(something)
}

fn main() {
    let saved = {
        let s = String::from("yippie");
        save_for_later(&s)
    };
}
error[E0597]: `s` does not live long enough --> src/bin/main_aeb777d71a9775f9cdeb01967c3423fe31409068.rs:12:24 | 12 | save_for_later(&s) | ---------------^^- | | | | | borrowed value does not live long enough | borrow later used here 13 | }; | - `s` dropped here while still borrowed For more information about this error, try `rustc --explain E0597`. error: could not compile `rust` due to previous error
trait MyTrait {}
impl MyTrait for String {}
impl<'a> MyTrait for &'a String {}

fn save_for_later<'a, T: MyTrait + 'a>(something: T) -> Box {
    Box::new(something)
}

fn main() {
    let saved = {
        let s = String::from("yippie");
        save_for_later(&s)
    };
}

Въпроси