Lifetimes

05 ноември 2018

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

Преговор

Преговор

Преговор

Преговор

Преговор

Преговор

Преговор

Жизнени цикли

Жизнени цикли

Интервалът в който една стойност е жива

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

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

Стойността живее докато е в scope - очевидно

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

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

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

1 2 3 4 5 6 7
{
    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
{
    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
{
    let r;
    {
        let x = 5;  //----+
        r = &x; // ----+  |
                //     |  |
    } // <----------------+
                // v---+
    println!("{}", r);
}

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

Иначе borrow checker-а ще изръмжи

1 2 3 4 5 6 7 8 9 10
{
    let r;
    {
        let x = 5;  //----+
        r = &x; // ----+  |
                //     |  |
    } // <----------------+
                // v---+
    println!("{}", r);
}
error[E0597]: `x` does not live long enough --> src/bin/main_fa10d38da86d8e94726f89cb8f0b106ec7b8be47.rs:6:9 | 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
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
{
    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
{
    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
/// Връща по-дългия от двата низа
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 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`
fn main() {}
/// Връща по-дългия от двата низа
fn longer(s1: &str, s2: &str) -> &str {
    if s1.len() > s2.len() { s1 } else { s2 }
}

Lifetime анотации

Lifetime анотации

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

Lifetime анотации

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

Lifetime анотации

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

Lifetime анотации

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

Lifetime анотации

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

Lifetime анотации

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

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 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`
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

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() {}

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
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

Как работи

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

Как работи

Ако първият аргумент е &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 { | ^ help: consider giving it a 'static lifetime: `&'static` | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from error[E0106]: missing lifetime specifier --> src/bin/main_4c57f10ba9573eb28198bf67d30d12e7c2c70820.rs:6:33 | 6 | fn longest(x: &str, y: &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 `x` or `y`
fn get_str() -> &str {
    // ...
unimplemented!()
}

fn longest(x: &str, y: &str) -> &str {
    // ...
unimplemented!()
}
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 {
        unimplemented!()
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1 2 3
struct Words {
    text: &str,
}
error[E0106]: missing lifetime specifier --> src/bin/main_afa8baa99e3366a4216f568a70ccdbcb9c7c3207.rs:2:11 | 2 | text: &str, | ^ expected lifetime parameter
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
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 }
    }
}

Lifetime elision в impl блок

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

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

Expanded:

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

Lifetime elision в impl блок

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

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

Expanded:

1 2 3 4 5
impl<'a> Words<'a> {
    fn new<'b>(text: &'b str) -> Words<'a> {
        Words { text }
    }
}
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements --> src/bin/main_154710e0a8f26410179dbda3cef5acb4d7942703.rs:6:9 | 6 | Words { text } | ^^^^^ | note: first, the lifetime cannot outlive the lifetime 'b as defined on the method body at 5:12... --> src/bin/main_154710e0a8f26410179dbda3cef5acb4d7942703.rs:5:12 | 5 | fn new<'b>(text: &'b str) -> Words<'a> { | ^^ note: ...so that reference does not outlive borrowed content --> src/bin/main_154710e0a8f26410179dbda3cef5acb4d7942703.rs:6:17 | 6 | Words { text } | ^^^^ note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 4:6... --> src/bin/main_154710e0a8f26410179dbda3cef5acb4d7942703.rs:4:6 | 4 | impl<'a> Words<'a> { | ^^ = note: ...so that the expression is assignable: expected Words<'a> found Words<'_>
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 метода

Live demo

Имплементация на 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
#[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?;
        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 new(text: &'a str) -> Self {
        Words { text: Some(text) }
    }

    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 метода

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

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

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

Всичко работи, но дали имаме правилните 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_d6797e8cf18756bda4efddc6ca761db598f5c6e8.rs:23:5 | 23 | words.next_word().unwrap() | -----^^^^^^^^^^^^^^^^^^^^^ | | | returns a value referencing data owned by the current function | `words` is borrowed here
#[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?;
let mut iter = text.splitn(2, char::is_whitespace);
match (iter.next(), iter.next()) {
(Some(word), rest) => {
self.text = rest;
Some(word)
},
_ => unreachable!()
}
}
}
fn hello() -> &'static str {
    let mut words = Words::new("hello world");
    words.next_word().unwrap()
}
fn main() {}

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

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14
impl<'a> Words<'a> {
    fn next_word<'b>(&'b mut self) -> Option<&'b 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<'b>(&'b mut self) -> Option<&'b 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 метода

Вместо това може да върнем резултат с 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> {
        // използваме lifetime 'a тук  ^^

        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 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?;
        let mut iter = text.splitn(2, char::is_whitespace);

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

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

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

Lifetimes & generics

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
impl ToJson for String {
    fn to_json(&self) -> String {
        format!("{:?}", self)
    }
}

fn save_for_later<T: ToJson>(to_json: T) -> Box<T> {
    Box::new(to_json)
}

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

    let inner = &*saved;
    println!("{}", inner.to_json());
}
"yippie"
trait ToJson { fn to_json(&self) -> String; }
impl ToJson for String {
    fn to_json(&self) -> String {
        format!("{:?}", self)
    }
}

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

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

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

Lifetimes & generics

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
impl<'a> ToJson for &'a String {
    fn to_json(&self) -> String {
        format!("{:?}", self)
    }
}

fn save_for_later<T: ToJson>(to_json: T) -> Box<T> {
    Box::new(to_json)
}

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

    let inner = &*saved;
    println!("{}", inner.to_json());
}
error[E0597]: `s` does not live long enough --> src/bin/main_dc1957335015d021f85f97c23a134c0684bf9882.rs:15:24 | 13 | let saved = { | ----- borrow later stored here 14 | let s = String::from("yippie"); 15 | save_for_later(&s) | ^^ borrowed value does not live long enough 16 | }; | - `s` dropped here while still borrowed
trait ToJson { fn to_json(&self) -> String; }
impl<'a> ToJson for &'a String {
    fn to_json(&self) -> String {
        format!("{:?}", self)
    }
}

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

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

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

Lifetimes & generics

Lifetimes & generics

Lifetimes & generics

Lifetimes & generics

Lifetimes & generics

Lifetimes & generics

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
fn save_for_later<T: ToJson + 'static>(to_json: T) -> Box<T> {
    Box::new(to_json)
}

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

    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_ff536077660f8e148afb3079b21d968dafc603cc.rs:21:24 | 21 | 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` 22 | }; | - `s` dropped here while still borrowed
trait ToJson { fn to_json(&self) -> String; }
impl ToJson for String {
fn to_json(&self) -> String { format!("{:?}", self) }
}
impl<'a> ToJson for &'a String {
fn to_json(&self) -> String { format!("{:?}", self) }
}

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

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

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

Въпроси