Lifetimes
02 ноември 2021
Преговор
Преговор
- конвертиране:
From,Into
Преговор
- конвертиране:
From,Into - парсене на низове:
FromStr,str::parse
Преговор
- конвертиране:
From,Into - парсене на низове:
FromStr,str::parse - error handling:
Option,Result
Преговор
- конвертиране:
From,Into - парсене на низове:
FromStr,str::parse - error handling:
Option,Result - error handling:
panic!
Преговор
- конвертиране:
From,Into - парсене на низове:
FromStr,str::parse - error handling:
Option,Result - error handling:
panic! - error handling:
try!, оператор?
Lifetimes
Lifetimes
Ще говорим за интервала в който една стойност е жива
fn main()
{
{
let x = 5; // --+
// |
} // <--------------+
}
fn main()
{
{
let x = 5; // --+
// |
} // <--------------+
}
Lifetime на променлива със собственост
Стойността живее докато променливата е в scope
fn main()
{
let s = String::from("five"); // --+
// |
} // <---------------------------------+
fn main()
{
let s = String::from("five"); // --+
// |
} // <---------------------------------+
Lifetime на променлива със собственост
Стойността живее докато променливата е в scope
fn main()
{
let s = String::from("five"); // --+
// |
// v------------------+
takes_ownership(s);
}
fn takes_ownership(s: String) {}
fn main()
{
let s = String::from("five"); // --+
// |
// v------------------+
takes_ownership(s);
}
fn takes_ownership(s: String) {}
Lifetime на референция
Референцията живее до мястото където е използвана за последно
fn main()
{
let x = 5;
let r = &x; // ----+
// |
// v---+
println!("{}", r);
}
5
fn main()
{
let x = 5;
let r = &x; // ----+
// |
// v---+
println!("{}", r);
}
Lifetime на референция
Референцията също така не може да надживее стойността, към която сочи
fn main()
{
let x = 5; //--------+
let r = &x; // ----+ |
// | |
// v---+ |
println!("{}", r); // |
} // <--------------------+
5
fn main()
{
let x = 5; //--------+
let r = &x; // ----+ |
// | |
// v---+ |
println!("{}", r); // |
} // <--------------------+
Lifetime на референция
Иначе borrow checker-а ще се скара
fn main()
{
let r;
{
let x = 5; //----+
r = &x; // ----+ |
// | |
} // <----------------+
// v---+
println!("{}", r);
}
Lifetime на референция
Иначе borrow checker-а ще се скара
fn main()
{
let r;
{
let x = 5; //----+
r = &x; // ----+ |
// | |
} // <----------------+
// v---+
println!("{}", r);
}
error[E0597]: `x` does not live long enough --> src/bin/main_fa10d38da86d8e94726f89cb8f0b106ec7b8be47.rs:6:13 | 6 | r = &x; // ----+ | | ^^ borrowed value does not live long enough 7 | // | | 8 | } // <----------------+ | - `x` dropped here while still borrowed 9 | // v---+ 10 | println!("{}", r); | - borrow later used here For more information about this error, try `rustc --explain E0597`. error: could not compile `rust` due to previous error
fn main()
{
let r;
{
let x = 5; //----+
r = &x; // ----+ |
// | |
} // <----------------+
// v---+
println!("{}", r);
}
Lifetimes
Нека пробваме с малко по-сложен пример…
fn main()
{
let x = 5;
let r2 = {
let r1 = &x;
&*r1
};
println!("{}", r2);
}
fn main()
{
let x = 5;
let r2 = {
let r1 = &x;
&*r1
};
println!("{}", r2);
}
Lifetimes
Нека пробваме с малко по-сложен пример… Работи
fn main()
{
let x = 5;
let r2 = {
let r1 = &x;
&*r1
};
println!("{}", r2);
}
5
fn main()
{
let x = 5;
let r2 = {
let r1 = &x;
&*r1
};
println!("{}", r2);
}
Lifetimes
Можем да означим колко дълго живеят x, r1 и r2
{
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
Как би работило в следния случай?
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 named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `s1` or `s2` help: consider introducing a named lifetime parameter | 4 | fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str { | ++++ ++ ++ ++ For more information about this error, try `rustc --explain E0106`. error: could not compile `rust` due to previous error
fn main() {}
/// Връща по-дългия от двата низа
fn longer(s1: &str, s2: &str) -> &str {
if s1.len() > s2.len() { s1 } else { s2 }
}
Lifetime анотации
Lifetime анотации
Какъв е проблема?
Lifetime анотации
Какъв е проблема?
- искаме да поддържаме функции като
longer
Lifetime анотации
Какъв е проблема?
- искаме да поддържаме функции като
longer - функции които връщат референции
Lifetime анотации
Какъв е проблема?
- искаме да поддържаме функции като
longer - функции които връщат референции
fn function(...) -> &X
Lifetime анотации
Какъв е проблема?
- искаме да поддържаме функции като
longer - функции които връщат референции
fn function(...) -> &X- следователно ни трябва начин да означим колко дълго живее върнатата референция
Lifetime анотации
Грешката ни казва, че трябва да означим дали върнатият резултат живее колкото параметъра s1 или колкото параметъра s2
/// Връща по-дългия от двата низа
fn longer(s1: &str, s2: &str) -> &str {
if s1.len() > s2.len() { s1 } else { s2 }
}
error[E0106]: missing lifetime specifier --> src/bin/main_33b4ac338e5075f0ff9097f3f20bad54061384df.rs:4:34 | 4 | fn longer(s1: &str, s2: &str) -> &str { | ---- ---- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `s1` or `s2` help: consider introducing a named lifetime parameter | 4 | fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str { | ++++ ++ ++ ++ For more information about this error, try `rustc --explain E0106`. error: could not compile `rust` due to previous error
fn main() {}
/// Връща по-дългия от двата низа
fn longer(s1: &str, s2: &str) -> &str {
if s1.len() > s2.len() { s1 } else { s2 }
}
Lifetime анотации
Отговорът: и трите живеят еднакво дълго
/// Връща по-дългия от двата низа
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 анотация- специален вид generic параметър
'a
'асе нарича lifetime параметър или lifetime анотация- специален вид generic параметър
- използва се да се означи колко дълго живее референция
'a
'асе нарича lifetime параметър или lifetime анотация- специален вид generic параметър
- използва се да се означи колко дълго живее референция
&'a X,&'b mut Y
'a
'асе нарича lifetime параметър или lifetime анотация- специален вид generic параметър
- използва се да се означи колко дълго живее референция
&'a X,&'b mut Y- може да е всякакъв идентификатор, но практиката е да са кратки или еднобуквени
'a
'асе нарича lifetime параметър или lifetime анотация- специален вид generic параметър
- използва се да се означи колко дълго живее референция
&'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
Как работи
(1) Ако за аргументите има само един 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
Как работи
(2) Ако първият аргумент е &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
Как работи
(3) Във всички останали случаи е грешка да не напишем 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 { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime | 1 | fn get_str() -> &'static str { | ~~~~~~~~ error[E0106]: missing lifetime specifier --> src/bin/main_4c57f10ba9573eb28198bf67d30d12e7c2c70820.rs:6:33 | 6 | fn longest(x: &str, y: &str) -> &str { | ---- ---- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y` help: consider introducing a named lifetime parameter | 6 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { | ++++ ++ ++ ++ For more information about this error, try `rustc --explain E0106`. error: could not compile `rust` due to 2 previous errors
fn get_str() -> &str {
// ...
unimplemented!()
}
fn longest(x: &str, y: &str) -> &str {
// ...
unimplemented!()
}
fn main() {}
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 {
todo!()
}
fn next_word(&mut self) -> Option<&str> {
todo!()
}
}
Референции в структури
Нека имаме структура, която връща думите от текст.
struct Words {
text: ??,
}
impl Words {
fn new(text: &str) -> Words {
todo!()
}
fn next_word(&mut self) -> Option<&str> {
todo!()
}
}
- какъв да е типа на полето
text?
Референции в структури
Нека имаме структура, която връща думите от текст.
struct Words {
text: ??,
}
impl Words {
fn new(text: &str) -> Words {
todo!()
}
fn next_word(&mut self) -> Option<&str> {
todo!()
}
}
- какъв да е типа на полето
text? - може да е тип който държи стойност, като
String, но тогава ще имаме излишно копиране
Референции в структури
Нека имаме структура, която връща думите от текст.
struct Words {
text: ??,
}
impl Words {
fn new(text: &str) -> Words {
todo!()
}
fn next_word(&mut self) -> Option<&str> {
todo!()
}
}
- какъв да е типа на полето
text? - може да е тип който държи стойност, като
String, но тогава ще имаме излишно копиране - може да пробваме да е референция
Референции в структури
struct Words {
text: &str,
}
error[E0106]: missing lifetime specifier --> src/bin/main_afa8baa99e3366a4216f568a70ccdbcb9c7c3207.rs:2:11 | 2 | text: &str, | ^ expected named lifetime parameter | help: consider introducing a named lifetime parameter | 1 ~ struct Words<'a> { 2 ~ text: &'a str, | For more information about this error, try `rustc --explain E0106`. error: could not compile `rust` due to previous error
struct Words {
text: &str,
}
fn main() {}
Референции в структури
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 For more information about this error, try `rustc --explain E0621`. error: could not compile `rust` due to previous error
struct Words<'a> {
text: &'a str,
}
impl<'a> Words<'a> {
fn new(text: &str) -> Self {
Words { text }
}
}
fn main() {}
Lifetime elision в impl блок
В случая Self означава Words<'a>:
impl<'a> Words<'a> {
fn new(text: &str) -> Words<'a> {
Words { text }
}
}
error[E0621]: explicit lifetime required in the type of `text` --> src/bin/main_392932f4ba7aa505606ed5e92a3e6a8c158cca2a.rs:6:9 | 5 | fn new(text: &str) -> Words<'a> { | ---- help: add explicit lifetime `'a` to the type of `text`: `&'a str` 6 | Words { text } | ^^^^^^^^^^^^^^ lifetime `'a` required For more information about this error, try `rustc --explain E0621`. error: could not compile `rust` due to previous error
struct Words<'a> {
text: &'a str,
}
impl<'a> Words<'a> {
fn new(text: &str) -> Words<'a> {
Words { text }
}
}
fn main() {}
Lifetime elision в impl блок
Expanded:
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 --> src/bin/main_154710e0a8f26410179dbda3cef5acb4d7942703.rs:6:9 | 6 | Words { text } | ^^^^^^^^^^^^^^ = note: expected `Words<'a>` found `Words<'_>` For more information about this error, try `rustc --explain E0495`. error: could not compile `rust` due to previous error
struct Words<'a> {
text: &'a str,
}
impl<'a> Words<'a> {
fn new<'b>(text: &'b str) -> Words<'a> {
Words { text }
}
}
fn main() {}
Lifetime elision в impl блок
Ако искаме да използваме Self, правилния вариант е:
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 метода
#[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> {
todo!()
}
}
#[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> {
todo!()
}
}
fn main() {}
Имплементация на next_word метода
Стъпка по стъпка
#[derive(Debug)]
struct Words<'a> {
text: Option<&'a str>,
}
impl<'a> Words<'a> {
fn next_word(&mut self) -> Option<&str> {
match self.text {
Some(text) => todo!(),
None => None,
}
}
}
#[derive(Debug)]
struct Words<'a> {
text: Option<&'a str>,
}
impl<'a> Words<'a> {
fn next_word(&mut self) -> Option<&str> {
match self.text {
Some(text) => todo!(),
None => None,
}
}
}
fn main() {}
Имплементация на next_word метода
Стъпка по стъпка
#[derive(Debug)]
struct Words<'a> {
text: Option<&'a str>,
}
impl<'a> Words<'a> {
fn next_word(&mut self) -> Option<&str> {
match self.text {
Some(text) => {
let iter = text.splitn(2, char::is_whitespace);
todo!()
}
None => None,
}
}
}
#[derive(Debug)]
struct Words<'a> {
text: Option<&'a str>,
}
impl<'a> Words<'a> {
fn next_word(&mut self) -> Option<&str> {
match self.text {
Some(text) => {
let iter = text.splitn(2, char::is_whitespace);
todo!()
}
None => None,
}
}
}
fn main() {}
Имплементация на next_word метода
Стъпка по стъпка
#[derive(Debug)]
struct Words<'a> {
text: Option<&'a str>,
}
impl<'a> Words<'a> {
fn next_word(&mut self) -> Option<&str> {
match self.text {
Some(text) => {
let iter = text.splitn(2, char::is_whitespace);
match (iter.next(), iter.next()) {
(Some(word), rest) => {
self.text = rest;
Some(word)
},
_ => unreachable!()
}
}
None => None,
}
}
}
error[E0596]: cannot borrow `iter` as mutable, as it is not declared as mutable --> src/bin/main_14b97060eb3c8fc0764dfe132f306c8ede9cee52.rs:12:24 | 10 | let iter = text.splitn(2, char::is_whitespace); | ---- help: consider changing this to be mutable: `mut iter` 11 | 12 | match (iter.next(), iter.next()) { | ^^^^ cannot borrow as mutable error[E0596]: cannot borrow `iter` as mutable, as it is not declared as mutable --> src/bin/main_14b97060eb3c8fc0764dfe132f306c8ede9cee52.rs:12:37 | 10 | let iter = text.splitn(2, char::is_whitespace); | ---- help: consider changing this to be mutable: `mut iter` 11 | 12 | match (iter.next(), iter.next()) { | ^^^^ cannot borrow as mutable For more information about this error, try `rustc --explain E0596`. error: could not compile `rust` due to 2 previous errors
#[derive(Debug)]
struct Words<'a> {
text: Option<&'a str>,
}
impl<'a> Words<'a> {
fn next_word(&mut self) -> Option<&str> {
match self.text {
Some(text) => {
let iter = text.splitn(2, char::is_whitespace);
match (iter.next(), iter.next()) {
(Some(word), rest) => {
self.text = rest;
Some(word)
},
_ => unreachable!()
}
}
None => None,
}
}
}
fn main() {}
Имплементация на next_word метода
Разкрасяване
#[derive(Debug)]
struct Words<'a> {
text: Option<&'a str>,
}
impl<'a> Words<'a> {
fn next_word(&mut self) -> Option<&str> {
let text = self.text?;
let mut iter = text.splitn(2, char::is_whitespace);
match (iter.next(), iter.next()) {
(Some(word), rest) => {
self.text = rest;
Some(word)
},
_ => unreachable!()
}
}
}
#[derive(Debug)]
struct Words<'a> {
text: Option<&'a str>,
}
impl<'a> Words<'a> {
fn next_word(&mut self) -> Option<&str> {
let text = self.text?;
let mut iter = text.splitn(2, char::is_whitespace);
match (iter.next(), iter.next()) {
(Some(word), rest) => {
self.text = rest;
Some(word)
},
_ => unreachable!()
}
}
}
fn main() {}
Имплементация на next_word метода
Тестване
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?;
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() {
let mut words = Words::new("hello world");
println!("{:?}", words.next_word());
println!("{:?}", words.next_word());
println!("{:?}", words.next_word());
}
Имплементация на next_word метода
А може и с unit test
#[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);
}
}
#[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);
}
}
fn main() {}
Имплементация на next_word метода
Lifetimes
Всичко работи, но дали имаме правилните lifetimes?
fn hello() -> &'static str {
let mut words = Words::new("hello world");
words.next_word().unwrap()
}
Имплементация на next_word метода
Lifetimes
Всичко работи, но дали имаме правилните 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 For more information about this error, try `rustc --explain E0515`. error: could not compile `rust` due to previous error
#[derive(Debug)]
struct Words<'a> {
text: Option<&'a str>,
}
impl<'a> Words<'a> {
fn new(text: &'a str) -> Self {
Words { text: Some(text) }
}
fn next_word(&mut self) -> Option<&str> {
let text = self.text?;
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 метода
Lifetimes
Това става, защото резултата от 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 метода
Lifetimes
Вместо това може да върнем резултат с 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());
}
Имплементация на 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<&'a 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<&'a 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() {}
Lifetimes & generics
Lifetimes & generics
trait MyTrait {}
impl MyTrait for String {}
struct Wrapper<T: MyTrait>(T);
fn save_for_later<T: MyTrait>(something: T) -> Wrapper<T> {
Wrapper(something)
}
fn main() {
let saved = {
let s = String::from("yippie");
save_for_later(s)
};
let inner = &saved.0;
println!("{}", inner);
}
yippie
trait MyTrait {}
impl MyTrait for String {}
struct Wrapper(T);
fn save_for_later(something: T) -> Wrapper {
Wrapper(something)
}
fn main() {
let saved = {
let s = String::from("yippie");
save_for_later(s)
};
let inner = &saved.0;
println!("{}", inner);
}
Lifetimes & generics
trait MyTrait {}
impl<'a> MyTrait for &'a String {}
struct Wrapper<T: MyTrait>(T);
fn save_for_later<T: MyTrait>(something: T) -> Wrapper<T> {
Wrapper(something)
}
fn main() {
let saved = {
let s = String::from("yippie");
save_for_later(&s)
};
let inner = &saved.0;
println!("{}", inner);
}
error[E0597]: `s` does not live long enough --> src/bin/main_944046f574413164dd1bf391cfeecc1e2abeb8d7.rs:13:24 | 11 | let saved = { | ----- borrow later stored here 12 | let s = String::from("yippie"); 13 | save_for_later(&s) | ^^ borrowed value does not live long enough 14 | }; | - `s` dropped here while still borrowed For more information about this error, try `rustc --explain E0597`. error: could not compile `rust` due to previous error
trait MyTrait {}
impl<'a> MyTrait for &'a String {}
struct Wrapper(T);
fn save_for_later(something: T) -> Wrapper {
Wrapper(something)
}
fn main() {
let saved = {
let s = String::from("yippie");
save_for_later(&s)
};
let inner = &saved.0;
println!("{}", inner);
}
Lifetimes & generics
- generic тип
Tможе да е референция
Lifetimes & generics
- generic тип
Tможе да е референция - тогава той има lifetime, макар че няма lifetime параметър
Lifetimes & generics
- generic тип
Tможе да е референция - тогава той има lifetime, макар че няма lifetime параметър
- и всеки тип който съдържа
Tсъщо има lifetime, защото съдържа референция
Lifetimes & generics
- generic тип
Tможе да е и тип с ownership
Lifetimes & generics
- generic тип
Tможе да е и тип с ownership - в този случай се приема, че
Tима lifetime'static
Lifetimes & generics
Aко искаме да запазим нещо за дълго можем да използваме ограничение 'static
fn save_for_later<T: MyTrait + 'static>(something: T) -> Wrapper<T> {
Wrapper(something)
}
fn main() {
let saved = {
let s = String::from("yippie");
save_for_later(s) // OK, T = String => T: 'static
};
}
trait MyTrait {}
impl MyTrait for String {}
impl<'a> MyTrait for &'a String {}
struct Wrapper(T);
fn save_for_later(something: T) -> Wrapper {
Wrapper(something)
}
fn main() {
let saved = {
let s = String::from("yippie");
save_for_later(s) // OK, T = String => T: 'static
};
}
Lifetimes & generics
Aко искаме да запазим нещо за дълго можем да използваме ограничение 'static
fn save_for_later<T: MyTrait + 'static>(something: T) -> Wrapper<T> {
Wrapper(something)
}
fn main() {
let saved = {
let s = String::from("yippie");
save_for_later(&s) // Err, T = &'a String => T: 'a
};
}
error[E0597]: `s` does not live long enough --> src/bin/main_47863cb4318a5526d1168585843d28102ada988b.rs:13:24 | 13 | save_for_later(&s) // Err, T = &'a String => T: 'a | ---------------^^- | | | | | borrowed value does not live long enough | argument requires that `s` is borrowed for `'static` 14 | }; | - `s` dropped here while still borrowed For more information about this error, try `rustc --explain E0597`. error: could not compile `rust` due to previous error
trait MyTrait {}
impl MyTrait for String {}
impl<'a> MyTrait for &'a String {}
struct Wrapper(T);
fn save_for_later(something: T) -> Wrapper {
Wrapper(something)
}
fn main() {
let saved = {
let s = String::from("yippie");
save_for_later(&s) // Err, T = &'a String => T: 'a
};
}
Lifetimes, generics & dynamic dispatch
Имаме подобен проблем ако искаме да ползваме dynamic dispatch
fn save_for_later<T: MyTrait>(something: T) -> Box<dyn MyTrait> {
Box::new(something)
}
fn main() {
// ...
}
error[E0310]: the parameter type `T` may not live long enough --> src/bin/main_b51c04a1a4b92edadf0fafcba2db31fdf9574870.rs:6:5 | 5 | fn save_for_later<T: MyTrait>(something: T) -> Box<dyn MyTrait> { | -- help: consider adding an explicit lifetime bound...: `T: 'static +` 6 | Box::new(something) | ^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds For more information about this error, try `rustc --explain E0310`. error: could not compile `rust` due to previous error
trait MyTrait {}
impl MyTrait for String {}
impl<'a> MyTrait for &'a String {}
fn save_for_later(something: T) -> Box {
Box::new(something)
}
fn main() {
// ...
}
Lifetimes, generics & dynamic dispatch
По подразбиране dyn Trait има ограничение 'static
fn save_for_later<T: MyTrait>(something: T) -> Box<dyn MyTrait + 'static> {
Box::new(something)
}
fn main() {
// ...
}
error[E0310]: the parameter type `T` may not live long enough --> src/bin/main_0c50acb87194fec8e19b1308b6c107246b96e4f2.rs:6:5 | 5 | fn save_for_later<T: MyTrait>(something: T) -> Box<dyn MyTrait + 'static> { | -- help: consider adding an explicit lifetime bound...: `T: 'static +` 6 | Box::new(something) | ^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds For more information about this error, try `rustc --explain E0310`. error: could not compile `rust` due to previous error
trait MyTrait {}
impl MyTrait for String {}
impl<'a> MyTrait for &'a String {}
fn save_for_later(something: T) -> Box {
Box::new(something)
}
fn main() {
// ...
}
Lifetimes, generics & dynamic dispatch
Можем да ограничим функцията да работи само със "static" типове
fn save_for_later<T: MyTrait + 'static>(something: T) -> Box<dyn MyTrait> {
Box::new(something)
}
fn main() {
// ...
}
trait MyTrait {}
impl MyTrait for String {}
impl<'a> MyTrait for &'a String {}
fn save_for_later(something: T) -> Box {
Box::new(something)
}
fn main() {
// ...
}
Lifetimes, generics & dynamic dispatch
Можем да сложим и различно ограничение
fn save_for_later<'a, T: MyTrait + 'a>(something: T) -> Box<dyn MyTrait + 'a> {
Box::new(something)
}
fn main() {
let s = String::from("yippie");
let saved = save_for_later(&s);
}
trait MyTrait {}
impl MyTrait for String {}
impl<'a> MyTrait for &'a String {}
fn save_for_later<'a, T: MyTrait + 'a>(something: T) -> Box {
Box::new(something)
}
fn main() {
let s = String::from("yippie");
let saved = save_for_later(&s);
}
Lifetimes, generics & dynamic dispatch
Разбира се, в този случай резултатът от функцията има ограничен lifetime
fn save_for_later<'a, T: MyTrait + 'a>(something: T) -> Box<dyn MyTrait + 'a> {
Box::new(something)
}
fn main() {
let saved = {
let s = String::from("yippie");
save_for_later(&s)
};
}
error[E0597]: `s` does not live long enough --> src/bin/main_aeb777d71a9775f9cdeb01967c3423fe31409068.rs:12:24 | 12 | save_for_later(&s) | ---------------^^- | | | | | borrowed value does not live long enough | borrow later used here 13 | }; | - `s` dropped here while still borrowed For more information about this error, try `rustc --explain E0597`. error: could not compile `rust` due to previous error
trait MyTrait {}
impl MyTrait for String {}
impl<'a> MyTrait for &'a String {}
fn save_for_later<'a, T: MyTrait + 'a>(something: T) -> Box {
Box::new(something)
}
fn main() {
let saved = {
let s = String::from("yippie");
save_for_later(&s)
};
}