Lifetimes

05 ноември 2024

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

Преговор

Преговор

Преговор

Lifetimes

Искаме да напишем следната функция

1 2 3 4 5 6 7 8 9 10
fn map_get(map: &HashMap<String, String>, key: &str) -> &str {
    todo!()
}

fn main() {
    let mut map = HashMap::new();
    map.insert(String::from("key"), String::from("value"));

    assert_eq!(map_get(&map, &"key"), "value");
}

Lifetimes

Искаме да поддържаме този код

1 2 3 4 5 6 7 8 9 10 11 12
fn main() {
    let mut map = HashMap::new();
    map.insert(String::from("key"), String::from("value"));

    let value = {
        let key = String::from("key");
        let value = map_get(&map, &key);
        value
    };

    println!("{:?}", value);
}
"value"
use std::collections::HashMap;
fn map_get<'a>(map: &'a HashMap, key: &str) -> &'a str { &map[key] }
fn main() {
    let mut map = HashMap::new();
    map.insert(String::from("key"), String::from("value"));

    let value = {
        let key = String::from("key");
        let value = map_get(&map, &key);
        value
    };

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

Lifetimes

Искаме да не поддържаме този код

1 2 3 4 5 6 7 8 9 10 11 12 13
fn main() {
    let key = String::from("key");

    let value = {
        let mut map = HashMap::new();
        map.insert(String::from("key"), String::from("value"));

        let value = map_get(&map, &key);
        value
    };

    println!("{:?}", value);
}
error[E0597]: `map` does not live long enough --> src/bin/main_595865e62fdb3466484d0dc9b3782a3ea73bd108.rs:10:29 | 6 | let value = { | ----- borrow later stored here 7 | let mut map = HashMap::new(); | ------- binding `map` declared here ... 10 | let value = map_get(&map, &key); | ^^^^ borrowed value does not live long enough 11 | value 12 | }; | - `map` dropped here while still borrowed For more information about this error, try `rustc --explain E0597`. error: could not compile `rust` (bin "main_595865e62fdb3466484d0dc9b3782a3ea73bd108") due to 1 previous error
use std::collections::HashMap;
fn map_get<'a>(map: &'a HashMap, key: &str) -> &'a str { &map[key] }
fn main() {
    let key = String::from("key");

    let value = {
        let mut map = HashMap::new();
        map.insert(String::from("key"), String::from("value"));

        let value = map_get(&map, &key);
        value
    };

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

Lifetimes

1 2 3
fn map_get(map: &HashMap<String, String>, key: &str) -> &str {
    &map[key]
}

Lifetimes

1 2 3
fn map_get(map: &HashMap<String, String>, key: &str) -> &str {
    &map[key]
}
error[E0106]: missing lifetime specifier --> src/bin/main_b9b959e73c67151f51d92f35cc299c121b242ef3.rs:3:57 | 3 | fn map_get(map: &HashMap<String, String>, key: &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 `map` or `key` help: consider introducing a named lifetime parameter | 3 | fn map_get<'a>(map: &'a HashMap<String, String>, key: &'a str) -> &'a str { | ++++ ++ ++ ++ For more information about this error, try `rustc --explain E0106`. error: could not compile `rust` (bin "main_b9b959e73c67151f51d92f35cc299c121b242ef3") due to 1 previous error
use std::collections::HashMap;
fn main() {}
fn map_get(map: &HashMap, key: &str) -> &str {
    &map[key]
}

Lifetimes

Трябва да означим с кой параметър е обвързан резултата

1 2 3 4 5 6 7 8 9 10 11
// връща два резена от подадения низ.
// резултата е обвързан с параметъра `text`
fn split_at(text: &str, index: usize) -> (&str, &str) { /* ... */ }

// връща стойност от речника по зададен ключ.
// резултата е обвързан с параметъра `map`
fn map_get(map: &HashMap<String, String>, key: &str) -> &str { /* ... */ }

// връща по-дългия от двата низа.
// резултата е обвързан и с `s1` и с `s2`
fn longer(s1: &str, s2: &str) -> &str { /* ... */ }

Lifetimes

Това се прави с lifetime анотации

1 2 3 4 5 6 7 8 9 10 11
// връща два резена от подадения низ.
// резултата е обвързан с параметъра `text`
fn split_at<'a>(text: &'a str, index: usize) -> (&'a str, &'a str) { /* ... */ }

// връща стойност от речника по зададен ключ.
// резултата е обвързан с параметъра `map`
fn map_get<'a>(map: &'a HashMap<String, String>, key: &str) -> &'a str { /* ... */ }

// връща по-дългия от двата низа.
// резултата е обвързан и с `s1` и с `s2`
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str { /* ... */ }

'a

'a

'a

'a

'a

'a

'a

'a

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

'a

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

'a

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

'a

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

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
{ if s1.len() >= s2.len() { s1 } else { s2 } }

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 11 12
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);    // --|--+    |
                                          //   |  |    |
        println!("{}", result);           //   |  |    |
    } // <-------------------------------------+--+    |
                                          //           |
} // <-------------------------------------------------+
дългият низ е дълъг
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str
{ if s1.len() >= s2.len() { s1 } else { s2 } }

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

Lifetimes - примери

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

1 2 3 4 5 6 7 8 9 10 11 12
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);    // --|--+    |
                                          //   |  |    |
        println!("{}", result);           //   |  |    |
    } // <-------------------------------------+--+    |
                                          //           |
} // <-------------------------------------------------+
дългият низ е дълъг
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str
{ if s1.len() >= s2.len() { s1 } else { s2 } }

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

Lifetimes - примери

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

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

fn main() {
    let s1 = String::from("дългият низ е дълъг"); // --+
    let result = {                                //   |
        let s2 = String::from("къс низ"); // --+       |
        longer(&s1, &s2)                  // --|--+    |
                                          //   |  |    |
    }; // <------------------------------------+--+    |
                                          //           |
    println!("{}", result);               //           |
} // <-------------------------------------------------+
error[E0597]: `s2` does not live long enough --> src/bin/main_6924ede250fd1bb1a0f74dfd79d60b37d4ab5cb1.rs:8:21 | 6 | let result = { // | | ------ borrow later stored here 7 | let s2 = String::from("къс низ"); // --+ | | -- binding `s2` declared here 8 | longer(&s1, &s2) // --|--+ | | ^^^ borrowed value does not live long enough 9 | // | | | 10 | }; // <------------------------------------+--+ | | - `s2` dropped here while still borrowed For more information about this error, try `rustc --explain E0597`. error: could not compile `rust` (bin "main_6924ede250fd1bb1a0f74dfd79d60b37d4ab5cb1") due to 1 previous error
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str
{ if s1.len() >= s2.len() { s1 } else { s2 } }

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

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

Как работи

(1) За всеки пропуснат 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

Как работи

(2) Ако за аргументите има само един 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

Как работи

(3) Ако първият аргумент е &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

Как работи

Във всички останали случаи е грешка да не напишем 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, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` | 1 | fn get_str() -> &'static str { | +++++++ help: instead, you are more likely to want to return an owned value | 1 | fn get_str() -> String { | ~~~~~~ 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` (bin "main_4c57f10ba9573eb28198bf67d30d12e7c2c70820") due to 2 previous errors
fn get_str() -> &str {
    // ...
unimplemented!()
}

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

Lifetimes

Обобщение

Lifetimes

Обобщение

Lifetimes

Обобщение

Lifetimes

Обобщение

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

1
let s: &'static str = "Низ литерал";
fn main() {
let s: &'static str = "Низ литерал";
}

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

1
let s: &'static str = "Низ литерал";
fn main() {
let s: &'static str = "Низ литерал";
}

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

1
let s: &'static str = "Низ литерал";
fn main() {
let s: &'static str = "Низ литерал";
}

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

1
let s: &'static str = "Низ литерал";
fn main() {
let s: &'static str = "Низ литерал";
}

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

1
let s: &'static str = "Низ литерал";
fn main() {
let s: &'static str = "Низ литерал";
}

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

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

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

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` (bin "main_afa8baa99e3366a4216f568a70ccdbcb9c7c3207") due to 1 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` (bin "main_20500598e5171c82492ec4a5e7146083406968cd") due to 1 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` (bin "main_392932f4ba7aa505606ed5e92a3e6a8c158cca2a") due to 1 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` (bin "main_154710e0a8f26410179dbda3cef5acb4d7942703") due to 1 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 метода

Тестване

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` (bin "main_1aa46f44383ccf6f88e4cd137658789c5d4d2beb") due to 1 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
impl<'a> Words<'a> {
    fn next_word<'b>(&'b mut self) -> Option<&'b str> {
        //           ^^^                     ^^^
    }
}

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

Lifetimes

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

1 2 3 4 5
impl<'a> Words<'a> {
    fn next_word(&mut self) -> Option<&'a str> {
        // използваме lifetime 'a тук  ^^
    }
}

Имплементация на 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
impl<'a> Words<'a> {
    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 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> {
        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() {}

Anonymous lifetimes

Можем да използваме анонимен lifetime '_

1 2 3 4 5 6 7 8 9
fn make_words_iter(text: &str) -> Words<'_> {
    Words::new(text)
}

fn print_next_word(words: &mut Words<'_>) {
    if let Some(word) = words.next_word() {
        println!("{}", word);
    }
}
fn main() {}
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> { todo!() }
}
fn make_words_iter(text: &str) -> Words<'_> {
    Words::new(text)
}

fn print_next_word(words: &mut Words<'_>) {
    if let Some(word) = words.next_word() {
        println!("{}", word);
    }
}

Anonymous lifetimes

Можем да използваме анонимен lifetime '_

1 2 3 4 5 6 7 8 9
fn make_words_iter(text: &str) -> Words<'_> {
    Words::new(text)
}

fn print_next_word(words: &mut Words<'_>) {
    if let Some(word) = words.next_word() {
        println!("{}", word);
    }
}
fn main() {}
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> { todo!() }
}
fn make_words_iter(text: &str) -> Words<'_> {
    Words::new(text)
}

fn print_next_word(words: &mut Words<'_>) {
    if let Some(word) = words.next_word() {
        println!("{}", word);
    }
}

Anonymous lifetimes

Можем да използваме анонимен lifetime '_

1 2 3 4 5 6 7 8 9
fn make_words_iter(text: &str) -> Words<'_> {
    Words::new(text)
}

fn print_next_word(words: &mut Words<'_>) {
    if let Some(word) = words.next_word() {
        println!("{}", word);
    }
}
fn main() {}
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> { todo!() }
}
fn make_words_iter(text: &str) -> Words<'_> {
    Words::new(text)
}

fn print_next_word(words: &mut Words<'_>) {
    if let Some(word) = words.next_word() {
        println!("{}", word);
    }
}

Lifetimes & generics

Lifetimes & generics

1 2 3 4 5 6 7 8 9 10 11
struct Pretty<T: Display>(T);

impl<T: Display> Display for Pretty<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "🌸🌹🌻 {} 🌻🌹🌸", self.0)
    }
}

fn prepare_pretty<T: Display>(something: T) -> Pretty<T> {
    Pretty(something)
}
use std::fmt::{self, Display};
struct Pretty(T);

impl Display for Pretty {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "🌸🌹🌻 {} 🌻🌹🌸", self.0)
    }
}

fn prepare_pretty(something: T) -> Pretty {
    Pretty(something)
}
fn main() {}

Lifetimes & generics

1 2 3 4 5 6 7 8 9
fn main() {
    // `String` имплементира `Display`
    let s: String = String::from("⚘⚘⚘");

    // Pretty<String>
    let pretty = prepare_pretty(s);

    println!("{}", pretty);
}
🌸🌹🌻 ⚘⚘⚘ 🌻🌹🌸
use std::fmt::{self, Display};
struct Pretty(T);
impl Display for Pretty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "🌸🌹🌻 {} 🌻🌹🌸", self.0)
}
}
fn prepare_pretty(something: T) -> Pretty {
Pretty(something)
}
fn main() {
    // `String` имплементира `Display`
    let s: String = String::from("⚘⚘⚘");

    // Pretty
    let pretty = prepare_pretty(s);

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

Lifetimes & generics

1 2 3 4 5 6 7 8 9
fn main() {
    // `&String` имплементира `Display`
    let s: String = String::from("⚘⚘⚘");

    // Pretty<&'a String>
    let pretty = prepare_pretty(&s);

    println!("{}", pretty);
}
🌸🌹🌻 ⚘⚘⚘ 🌻🌹🌸
use std::fmt::{self, Display};
struct Pretty(T);
impl Display for Pretty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "🌸🌹🌻 {} 🌻🌹🌸", self.0)
}
}
fn prepare_pretty(something: T) -> Pretty {
Pretty(something)
}
fn main() {
    // `&String` имплементира `Display`
    let s: String = String::from("⚘⚘⚘");

    // Pretty<&'a String>
    let pretty = prepare_pretty(&s);

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

Lifetimes & generics

Lifetimes & generics

Lifetimes & generics

Lifetimes & generics

Lifetimes & generics

1 2 3
Т = &'a u32       ⇒  T: 'a
T = &'static u32  ⇒  T: 'static
T = u32           ⇒  T: 'static

Lifetimes & generics

Това може да има значение, ако се опитаме да запазим generic тип като trait object

1 2 3 4 5 6 7 8 9
fn wrap<T: Debug>(something: T) -> Box<dyn Debug> {
    Box::new(something)
}

fn main() {
    let s = String::from("⚘⚘⚘");
    let boxed = wrap(s);
    println!("{:?}", boxed);
}
error[E0310]: the parameter type `T` may not live long enough --> src/bin/main_3d563f3098ca4e82dc7484659c796a54dd2210c0.rs:3:5 | 3 | Box::new(something) | ^^^^^^^^^^^^^^^^^^^ | | | the parameter type `T` must be valid for the static lifetime... | ...so that the type `T` will meet its required lifetime bounds | help: consider adding an explicit lifetime bound | 2 | fn wrap<T: Debug + 'static>(something: T) -> Box<dyn Debug> { | +++++++++ For more information about this error, try `rustc --explain E0310`. error: could not compile `rust` (bin "main_3d563f3098ca4e82dc7484659c796a54dd2210c0") due to 1 previous error
use std::fmt::Debug;
fn wrap(something: T) -> Box {
    Box::new(something)
}

fn main() {
    let s = String::from("⚘⚘⚘");
    let boxed = wrap(s);
    println!("{:?}", boxed);
}

Lifetimes & generics

Можем да се ограничим да работим само с типове T: 'static

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

fn main() {
    let s = String::from("⚘⚘⚘");
    let boxed = wrap(s);
    println!("{:?}", boxed);
}
"⚘⚘⚘"
use std::fmt::Debug;
fn wrap(something: T) -> Box {
    Box::new(something)
}

fn main() {
    let s = String::from("⚘⚘⚘");
    let boxed = wrap(s);
    println!("{:?}", boxed);
}

Lifetimes & generics

Можем да върнем Box, който има ограничен lifetime

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

fn main() {
    let s = String::from("⚘⚘⚘");
    let boxed = wrap(s);
    println!("{:?}", boxed);
}
"⚘⚘⚘"
use std::fmt::Debug;
fn wrap<'a, T: Debug + 'a>(something: T) -> Box {
    Box::new(something)
}

fn main() {
    let s = String::from("⚘⚘⚘");
    let boxed = wrap(s);
    println!("{:?}", boxed);
}

Въпроси