Lifetimes
05 ноември 2018
Административни неща
- първото домашно приключи
Преговор
Преговор
- конвертиране:
From,Into
Преговор
- конвертиране:
From,Into - парсене на низове:
FromStr, str::parse
Преговор
- конвертиране:
From,Into - парсене на низове:
FromStr, str::parse - error handling:
Result
Преговор
- конвертиране:
From,Into - парсене на низове:
FromStr, str::parse - error handling:
Result - error handling:
panic!
Преговор
- конвертиране:
From,Into - парсене на низове:
FromStr, str::parse - error handling:
Result - error handling:
panic! - error handling:
try!, оператор?
Преговор
- конвертиране:
From,Into - парсене на низове:
FromStr, str::parse - error handling:
Result - error handling:
panic! - error handling:
try!, оператор? - IO:
Read,Write,BufRead,BufWrite
Жизнени цикли

Жизнени цикли
Интервалът в който една стойност е жива
{
{
let x = 5; // --+
// |
} // <--------------+
}
fn main()
{
{
let x = 5; // --+
// |
} // <--------------+
}
Lifetime на променлива със собственост
Стойността живее докато е в scope - очевидно
{
let s = String::from("five"); // --+
// |
} // <---------------------------------+
fn main()
{
let s = String::from("five"); // --+
// |
} // <---------------------------------+
Lifetime на референция
Референцията живее до мястото където е използвана за последно
{
let x = 5;
let r = &x; // ----+
// |
// v---+
println!("{}", r);
}
5
fn main()
{
let x = 5;
let r = &x; // ----+
// |
// v---+
println!("{}", r);
}
Lifetime на референция
Референцията също така не може да надживее стойността към която сочи
{
let x = 5; //--------+
let r = &x; // ----+ |
// | |
// v---+ |
println!("{}", r); // |
} // <--------------------+
5
fn main()
{
let x = 5; //--------+
let r = &x; // ----+ |
// | |
// v---+ |
println!("{}", r); // |
} // <--------------------+
Lifetime на референция
Иначе borrow checker-а ще изръмжи
{
let r;
{
let x = 5; //----+
r = &x; // ----+ |
// | |
} // <----------------+
// v---+
println!("{}", r);
}
Lifetime на референция
Иначе borrow checker-а ще изръмжи
{
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
Нека пробваме с малко по-сложен пример
{
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
Работи
{
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
{
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
{
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
- можем ли да счупим borrow checker-а?
Lifetimes
- можем ли да счупим borrow checker-а?
- нека си дефинираме следната функция
/// Връща по-дългия от двата низа
fn longer(s1: &str, s2: &str) -> &str {
if s1.len() > s2.len() { s1 } else { s2 }
}
Lifetimes
- можем ли да счупим borrow checker-а?
- нека си дефинираме следната функция
/// Връща по-дългия от двата низа
fn longer(s1: &str, s2: &str) -> &str {
if s1.len() > s2.len() { s1 } else { s2 }
}
- ок?
Lifetimes
Как би работило в следния случай?
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
Как би работило в следния случай?
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
- не може валидността на функцията да зависи от това какви аргументи ѝ подаваме
- и действително тази функция не се компилира
/// Връща по-дългия от двата низа
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 анотации
Какъв е проблема
- искаме да поддържаме функции като
longer
Lifetime анотации
Какъв е проблема
- искаме да поддържаме функции като
longer - функции които връщат референции
Lifetime анотации
Какъв е проблема
- искаме да поддържаме функции като
longer - функции които връщат референции
fn function(...) -> &X
Lifetime анотации
Какъв е проблема
- искаме да поддържаме функции като
longer - функции които връщат референции
fn function(...) -> &X- следователно ни трябва начин да означим колко дълго живее върнатата референция
Lifetime анотации
Грешката ни казва, че трябва да означим дали върнатият резултат живее колкото параметъра a или колкото параметъра b
/// Връща по-дългия от двата низа
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 анотации
Отговорът: и трите живеят еднакво дълго
/// Връща по-дългия от двата низа
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
'асе нарича lifetime параметър или lifetime анотация
'a
'асе нарича lifetime параметър или lifetime анотация- използва се да се означи колко дълго живее референция
'a
'асе нарича lifetime параметър или lifetime анотация- използва се да се означи колко дълго живее референция
&'a X,&'b mut Y
'a
'асе нарича lifetime параметър или lifetime анотация- използва се да се означи колко дълго живее референция
&'a X,&'b mut Y- може да е всякакъв идентификатор, но практиката е да са кратки или еднобуквени
'a
'асе нарича lifetime параметър или lifetime анотация- използва се да се означи колко дълго живее референция
&'a X,&'b mut Y- може да е всякакъв идентификатор, но практиката е да са кратки или еднобуквени
&'my_lifetime X
'a
fn foo<'a>(x: &'a str) { }
fn foo<'a>(x: &'a str) { }
fn main() {}
- отделно
x: &'a strне ни дава информация
'a
fn foo<'a>(x: &'a str) { }
fn foo<'a>(x: &'a str) { }
fn main() {}
- отделно
x: &'a strне ни дава информация - измислили сме си име
aза периода за който живее референциятаx
'a
fn foo<'a>(x: &'a str, y: &'a str) { }
fn foo<'a>(x: &'a str, y: &'a str) { }
fn main() {}
- заедно
x: &'a strиy: &'a strзадават ограничение
'a
fn foo<'a>(x: &'a str, y: &'a str) { }
fn foo<'a>(x: &'a str, y: &'a str) { }
fn main() {}
- заедно
x: &'a strиy: &'a strзадават ограничение xживее колкотоy
'a
fn foo<'a>(x: &'a str, y: &'a str) { }
fn foo<'a>(x: &'a str, y: &'a str) { }
fn main() {}
- заедно
x: &'a strиy: &'a strзадават ограничение xживее колкотоy- (в случая отново не получаваме много информация, защото и двете са аргументи)
Lifetimes - примери
Функцията trim връща резултат който живее колкото аргумента
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 връща резултат който живее колкото общия период в който живеят двата ѝ аргумента.
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 - примери
Ако двата аргумента живеят различно - ще се вземе по-малкия период
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 - примери
Ако двата аргумента живеят различно - ще се вземе по-малкия период
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); // --|--+ |
// | | |
} // <-------------------------------------+--+ |
} // <-------------------------------------------------+
- има автоматично конвертиране от по-голям до по-малък lifetime
Lifetimes - примери
Не е нужно всички lifetime-и да са еднакви
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 параметър
Lifetime elision
- всяка референция има lifetime параметър
- но не е задължително винаги да ги пишем
Lifetime elision
- всяка референция има lifetime параметър
- но не е задължително винаги да ги пишем
- когато ситуацията не е двусмислена може да се пропуснат
Lifetime elision
- всяка референция има lifetime параметър
- но не е задължително винаги да ги пишем
- когато ситуацията не е двусмислена може да се пропуснат
- това се нарича lifetime elision
Lifetime elision
Следните дефиниции са еквивалентни:
fn trim(s: &str) -> &str {
// ...
}
fn trim(s: &str) -> &str {
// ...
unimplemented!()
}
fn main() {}
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
Lifetime elision
Кога трябва да пишем lifetimes?
- блок код
- никога
- компилаторът винаги има всичката нужна информация да определи правилния lifetime
- дефиниция на функция
- понякога
- тук се прилага lifetime elision
Lifetime elision
Кога трябва да пишем lifetimes?
- блок код
- никога
- компилаторът винаги има всичката нужна информация да определи правилния lifetime
- дефиниция на функция
- понякога
- тук се прилага lifetime elision
- структура
- винаги
Lifetime elision
Как работи
За всеки пропуснат lifetime в аргументите се добавя нов lifetime параметър
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 в резултата
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 в резултата
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 анотацията.
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
Обобщение
- всички референции имат lifetime параметър (
&'a X)
Lifetimes
Обобщение
- всички референции имат lifetime параметър (
&'a X) - показваме че две референции живеят еднакво дълго като им зададем еднакъв lifetime параметър
Lifetimes
Обобщение
- всички референции имат lifetime параметър (
&'a X) - показваме че две референции живеят еднакво дълго като им зададем еднакъв lifetime параметър
- в много случаи lifetime параметрите могат да не се пишат благодарение на lifetime elision
Статичен живот

Статичен живот
Специалният lifetime 'static.
Оказва че променливата живее за целия живот на програмата.
let s: &'static str = "I have a static lifetime.";
fn main() {
let s: &'static str = "I have a static lifetime.";
}
Референции в структури
Нека имаме структура, която връща думите от текст.
struct Words {
text: ??,
}
impl Words {
fn new(text: &str) -> Words {
unimplemented!()
}
fn next_word(&mut self) -> Option<&str> {
unimplemented!()
}
}
Референции в структури
Нека имаме структура, която връща думите от текст.
struct Words {
text: ??,
}
impl Words {
fn new(text: &str) -> Words {
unimplemented!()
}
fn next_word(&mut self) -> Option<&str> {
unimplemented!()
}
}
- какъв да е типа на полето
text?
Референции в структури
Нека имаме структура, която връща думите от текст.
struct Words {
text: ??,
}
impl Words {
fn new(text: &str) -> Words {
unimplemented!()
}
fn next_word(&mut self) -> Option<&str> {
unimplemented!()
}
}
- какъв да е типа на полето
text? - може да е тип който държи стойност, като
String, но тогава ще имаме излишно копиране
Референции в структури
Нека имаме структура, която връща думите от текст.
struct Words {
text: ??,
}
impl Words {
fn new(text: &str) -> Words {
unimplemented!()
}
fn next_word(&mut self) -> Option<&str> {
unimplemented!()
}
}
- какъв да е типа на полето
text? - може да е тип който държи стойност, като
String, но тогава ще имаме излишно копиране - а може и да е референция
Референции в структури
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() {}
Референции в структури
struct Words<'a> {
text: &'a str,
}
struct Words<'a> {
text: &'a str,
}
fn main() {}
Референции в структури
Съответно трябва да добавим lifetime параметъра и към impl блока
#[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() {}
Референции в структури
Животът на структурата е ограничен до това колко живее обектът, от който сме взели референция
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?
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:
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:
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() {}
- aлгоритъмът не взима под внимание lifetime-а
'a
Lifetime elision в impl блок
Expanded:
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() {}
- aлгоритъмът не взима под внимание lifetime-а
'a - пропуснати lifetime параметри на структури се попълват по същия начин като референциите
Lifetime elision в impl блок
Ами ако използваме Self?
impl<'a> Words<'a> {
fn new(text: &str) -> Self {
Words { text }
}
}
Lifetime elision в impl блок
Ами ако използваме Self?
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>:
impl<'a> Words<'a> {
fn new(text: &str) -> Words<'a> {
Words { text }
}
}
Lifetime elision в impl блок
В случая Self означава Words<'a>:
impl<'a> Words<'a> {
fn new(text: &str) -> Words<'a> {
Words { text }
}
}
Expanded:
impl<'a> Words<'a> {
fn new<'b>(text: &'b str) -> Words<'a> {
Words { text }
}
}
Lifetime elision в impl блок
В случая Self означава Words<'a>:
impl<'a> Words<'a> {
fn new(text: &str) -> Words<'a> {
Words { text }
}
}
Expanded:
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, правилния вариант е:
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 метода
Финален код
#[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?
fn hello() -> &'static str {
let mut words = Words::new("hello world");
words.next_word().unwrap()
}
Имплементация на next_word метода
Всичко работи, но дали имаме правилните lifetimes?
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).
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).
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
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
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
- шаблонен тип
Tможе да е референция
Lifetimes & generics
- шаблонен тип
Tможе да е референция - тогава той има lifetime, макар че няма lifetime параметър
Lifetimes & generics
- шаблонен тип
Tможе да е референция - тогава той има lifetime, макар че няма lifetime параметър
- и всеки тип който съдържа
Tсъщо има lifetime, защото съдържа референция
Lifetimes & generics
- приема се, че тип който има собственост над стойността която съдържа има lifetime
'static
Lifetimes & generics
- приема се, че тип който има собственост над стойността която съдържа има lifetime
'static - ако искаме да запазим нещо за дълго можем да използваме ограничение
'static
Lifetimes & generics
- приема се, че тип който има собственост над стойността която съдържа има lifetime
'static - ако искаме да запазим нещо за дълго можем да използваме ограничение
'static
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
};
}