Lifetimes
30 октомври 2018
Административни неща
Административни неща
- OpenFest
Административни неща
- OpenFest
- Rust 1.30
Административни неща
- OpenFest
- Rust 1.30
rustup update
Административни неща
- OpenFest
- Rust 1.30
rustup update
- блог пост
Преговор
Преговор
- конвертиране:
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
Lifetimes
Виждали сме lifetimes
&'static str
Виждали сме lifetimes
&'static str
&'a [u8]
Виждали сме lifetimes
&'static str
&'a [u8]
Жизнени цикли
Виждали сме как работи borrow checker-а
{
let x = 5;
let r = &x;
println!("{}", r);
}
5
Жизнени цикли
Виждали сме как работи borrow checker-а
{
let r = {
let x = 5;
&x
};
println!("{}", r);
}
error[E0597]: `x` does not live long enough --> /src/main_1.rs:6:10 | 6 | &x | ^ borrowed value does not live long enough 7 | }; | - `x` dropped here while still borrowed ... 10 | } | - borrowed value needs to live until here
Жизнени цикли
Виждали сме как работи borrow checker-а
{
let x = 5;
let r1 = &x;
let r2 = &*r1;
println!("{}", r2);
}
5
Жизнени цикли
Ами сега?
{
let x = 5;
let r2 = {
let r1 = &x;
&*r1
};
println!("{}", r2);
}
Жизнени цикли
Ами сега?
{
let x = 5;
let r2 = {
let r1 = &x;
&*r1
};
println!("{}", r2);
}
5
Очевидно работи, но защо?
Жизнени цикли
Защо работи?
{
let x = 5; // -------+-- x
// |
let r2 = { // |
let r1 = &x; // ----+--|-- r1
&*r1 // -+--|--|-- r2
}; // -|--+ |
// | |
println!("{}", r2); // | |
// -+ |
} // -------+
Жизнени цикли
Защо работи?
{
let x = 5; // -------+-- x
// |
let r2 = { // |
let r1 = &x; // ----+--|-- r1
&*r1 // -+--|--|-- r2
}; // -|--+ |
// | |
println!("{}", r2); // | |
// -+ |
} // -------+
- променливата
x
живее от ред 2 до ред 11
Жизнени цикли
Защо работи?
{
let x = 5; // -------+-- x
// |
let r2 = { // |
let r1 = &x; // ----+--|-- r1
&*r1 // -+--|--|-- r2
}; // -|--+ |
// | |
println!("{}", r2); // | |
// -+ |
} // -------+
- променливата
x
живее от ред 2 до ред 11 - променливите
r1
иr2
са валидни за синтактичните scope-ове в които са дефинирани
Жизнени цикли
Защо работи?
{
let x = 5; // -------+-- x
// |
let r2 = { // |
let r1 = &x; // ----+--|-- *r1
&*r1 // -+--|--|-- *r2
}; // | | |
// | | |
println!("{}", r2); // | | |
// | | |
} // -+--+--+
Жизнени цикли
Защо работи?
{
let x = 5; // -------+-- x
// |
let r2 = { // |
let r1 = &x; // ----+--|-- *r1
&*r1 // -+--|--|-- *r2
}; // | | |
// | | |
println!("{}", r2); // | | |
// | | |
} // -+--+--+
- стойността към която сочи
r1
живее колкотоx
Жизнени цикли
Защо работи?
{
let x = 5; // -------+-- x
// |
let r2 = { // |
let r1 = &x; // ----+--|-- *r1
&*r1 // -+--|--|-- *r2
}; // | | |
// | | |
println!("{}", r2); // | | |
// | | |
} // -+--+--+
- стойността към която сочи
r1
живее колкотоx
*r1
живее колкотоx
Жизнени цикли
Защо работи?
{
let x = 5; // -------+-- x
// |
let r2 = { // |
let r1 = &x; // ----+--|-- *r1
&*r1 // -+--|--|-- *r2
}; // | | |
// | | |
println!("{}", r2); // | | |
// | | |
} // -+--+--+
- стойността към която сочи
r1
живее колкотоx
*r1
живее колкотоx
&*r1
създава псевдоним, който живее колкотоx
Жизнени цикли и функции
Какво става ако имаме функция
fn longer(x: &str, y: &str) -> &str {
if x.len() >= y.len() {
x
} else {
y
}
}
Жизнени цикли и функции
Какво става ако имаме функция
fn longer(x: &str, y: &str) -> &str {
if x.len() >= y.len() {
x
} else {
y
}
}
error[E0106]: missing lifetime specifier --> /src/main_6.rs:2:32 | 2 | fn longer(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`
Жизнени цикли и функции
Колко трябва да живее стойността, към която сочи result
?
{
let s1 = String::from("long string is long");
{
let s2 = String::from("short string");
let result = longer(s1.as_str(), s2.as_str());
}
}
Жизнени цикли и функции
Колко трябва да живее стойността, към която сочи result
?
{
let s1 = String::from("long string is long");
{
let s2 = String::from("short string");
let result = longer(s1.as_str(), s2.as_str());
}
}
result
е псевдоним, значи сочи към стойност
Жизнени цикли и функции
Колко трябва да живее стойността, към която сочи result
?
{
let s1 = String::from("long string is long");
{
let s2 = String::from("short string");
let result = longer(s1.as_str(), s2.as_str());
}
}
result
е псевдоним, значи сочи към стойност- но не знаем колко живее тази стойност
Жизнени цикли и функции
Колко трябва да живее стойността, към която сочи result
?
{
let s1 = String::from("long string is long");
{
let s2 = String::from("short string");
let result = longer(s1.as_str(), s2.as_str());
}
}
result
е псевдоним, значи сочи към стойност- но не знаем колко живее тази стойност
- ако
s1
е по-големият низ,result
трябва да живее колкото него
Жизнени цикли и функции
Колко трябва да живее стойността, към която сочи result
?
{
let s1 = String::from("long string is long");
{
let s2 = String::from("short string");
let result = longer(s1.as_str(), s2.as_str());
}
}
result
е псевдоним, значи сочи към стойност- но не знаем колко живее тази стойност
- ако
s1
е по-големият низ,result
трябва да живее колкото него - ако
s2
е по-големият низ,result
трябва да живее колкото него
Жизнени цикли и функции
Колко трябва да живее стойността, към която сочи result
?
{
let s1 = String::from("long string is long");
{
let s2 = String::from("short string");
let result = longer(s1.as_str(), s2.as_str());
}
}
result
е псевдоним, значи сочи към стойност- но не знаем колко живее тази стойност
- ако
s1
е по-големият низ,result
трябва да живее колкото него - ако
s2
е по-големият низ,result
трябва да живее колкото него - но borrow checker-а работи по време на компилация
Lifetime анотации
fn longer<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() >= y.len() {
x
} else {
y
}
}
Lifetime анотации
fn longer<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() >= y.len() {
x
} else {
y
}
}
'а
се нарича lifetime анотация
Lifetime анотации
fn longer<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() >= y.len() {
x
} else {
y
}
}
'а
се нарича lifetime анотация- специален вид generic параметър
Lifetime анотации
fn longer<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() >= y.len() {
x
} else {
y
}
}
'а
се нарича lifetime анотация- специален вид generic параметър
- означава scope (не задължително синтактичен), за който живее някоя стойност
Lifetime анотации
fn foo<'a>(x: &'a str) { }
Lifetime анотации
fn foo<'a>(x: &'a str) { }
- отделно
x: &'a str
не дава информация
Lifetime анотации
fn foo<'a>(x: &'a str) { }
- отделно
x: &'a str
не дава информация - стойността зад
x
живее в някакъв scope, който сме кръстили'a
Lifetime анотации
fn foo<'a>(x: &'a str) { }
- отделно
x: &'a str
не дава информация - стойността зад
x
живее в някакъв scope, който сме кръстили'a
- няма как да означим, че
'a
e определен scope
Lifetime анотации
fn foo<'a>(x: &'a str) { }
- отделно
x: &'a str
не дава информация - стойността зад
x
живее в някакъв scope, който сме кръстили'a
- няма как да означим, че
'a
e определен scope
fn foo<'a>(x: &'a str, y: &'a str) { }
Lifetime анотации
fn foo<'a>(x: &'a str) { }
- отделно
x: &'a str
не дава информация - стойността зад
x
живее в някакъв scope, който сме кръстили'a
- няма как да означим, че
'a
e определен scope
fn foo<'a>(x: &'a str, y: &'a str) { }
- заедно
x: &'a str
иy: &'a str
задават ограничение
Lifetime анотации
fn foo<'a>(x: &'a str) { }
- отделно
x: &'a str
не дава информация - стойността зад
x
живее в някакъв scope, който сме кръстили'a
- няма как да означим, че
'a
e определен scope
fn foo<'a>(x: &'a str, y: &'a str) { }
- заедно
x: &'a str
иy: &'a str
задават ограничение - стойностите зад
x
иy
трябва да живеят едновременно в scope'a
Lifetime анотации
fn foo<'a>(x: &'a str) { }
- отделно
x: &'a str
не дава информация - стойността зад
x
живее в някакъв scope, който сме кръстили'a
- няма как да означим, че
'a
e определен scope
fn foo<'a>(x: &'a str, y: &'a str) { }
- заедно
x: &'a str
иy: &'a str
задават ограничение - стойностите зад
x
иy
трябва да живеят едновременно в scope'a
- при извикване компилатора ще намери най-големия такъв scope, ако съществува
Lifetime анотации
fn longer<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() >= y.len() { x } else { y }
}
{
let s1 = String::from("long string is long"); // ----+-- s1
{ // |
let s2 = String::from("short string"); // --+-|-- s2, 'a
let result = longer(s1.as_str(), s2.as_str()); // | |
println!("The longer string is: {}", result); // | |
} // --+ |
} // ----+
Lifetime анотации
fn longer<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() >= y.len() { x } else { y }
}
{
let s1 = String::from("long string is long"); // ----+-- s1
let result = { // |
let s2 = String::from("short string"); // --+-|-- s2, 'a
longer(s1.as_str(), s2.as_str()) // | |
}; // --+ |
println!("The longer string is: {}", result); // |
} // ----+
error[E0597]: `s2` does not live long enough --> /src/main_12.rs:12:29 | 12 | longer(s1.as_str(), s2.as_str()) // | | | ^^ borrowed value does not live long enough 13 | }; // --+ | | - `s2` dropped here while still borrowed 14 | println!("The longer string is: {}", result); // | 15 | } // ----+ | - borrowed value needs to live until here
Lifetime анотации
Не е задължително всички lifetime-и да са еднакви
fn first_occurrence<'a, 'b>(s: &'a str, pattern: &'b str) -> Option<&'a str> {
s.matches(pattern).next()
}
Lifetime elision
Виждали сме и функции, които връщат референция и не използват lifetime анотации
fn trim(s: &str) -> &str {
// ...
}
Защо в някои случаи работи, а в други не?
Lifetime elision
Следните дефиниции са еквивалентни:
fn trim(s: &str) -> &str {
// ...
}
fn trim<'a>(s: &'a str) -> &'a str {
// ...
}
Lifetime elision
Следните дефиниции са еквивалентни:
fn trim(s: &str) -> &str {
// ...
}
fn trim<'a>(s: &'a str) -> &'a str {
// ...
}
- всяка референция винаги има lifetime
Lifetime elision
Следните дефиниции са еквивалентни:
fn trim(s: &str) -> &str {
// ...
}
fn trim<'a>(s: &'a str) -> &'a str {
// ...
}
- всяка референция винаги има lifetime
- когато ситуацията не е двусмислена, можем да ги пропуснем и компилаторът сам ще ги добави
Lifetime elision
Следните дефиниции са еквивалентни:
fn trim(s: &str) -> &str {
// ...
}
fn trim<'a>(s: &'a str) -> &'a str {
// ...
}
- всяка референция винаги има lifetime
- когато ситуацията не е двусмислена, можем да ги пропуснем и компилаторът сам ще ги добави
- това се нарича lifetime elision
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/main_16.rs:3:17 | 3 | fn get_str() -> &str { | ^ expected 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 giving it a 'static lifetime error[E0106]: missing lifetime specifier --> /src/main_16.rs:8:33 | 8 | 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`
Lifetime coersion
Променливите автоматично минават от по-голям към по-малък lifetime, ако се налага
fn longer<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() >= y.len() { x } else { y }
}
let s1 = "long string is long"; // &'static str
{
let string = String::from("short"); // --+- 'a
let s2 = string.as_str(); // --+- &'a str
let result = longer(s1, s2); // | s1: &'static str -> &'a str
} // --+
Статичен живот
Статичен живот
Специалният lifetime 'static
.
Оказва че променливата живее за целия живот на програмата.
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) -> &str {
unimplemented!()
}
}
Референции в структури
Нека имаме структура, която връща думите от текст.
struct Words {
text: ??,
}
impl Words {
fn new(text: &str) -> Words {
unimplemented!()
}
fn next_word(&mut self) -> &str {
unimplemented!()
}
}
- какъв да е типа на полето
text
?
Референции в структури
Нека имаме структура, която връща думите от текст.
struct Words {
text: ??,
}
impl Words {
fn new(text: &str) -> Words {
unimplemented!()
}
fn next_word(&mut self) -> &str {
unimplemented!()
}
}
- какъв да е типа на полето
text
? - може да е тип който държи стойност, като
String
, но тогава ще имаме излишно копиране
Референции в структури
Нека имаме структура, която връща думите от текст.
struct Words {
text: ??,
}
impl Words {
fn new(text: &str) -> Words {
unimplemented!()
}
fn next_word(&mut self) -> &str {
unimplemented!()
}
}
- какъв да е типа на полето
text
? - може да е тип който държи стойност, като
String
, но тогава ще имаме излишно копиране - а може и да е референция
Референции в структури
struct Words {
text: &str,
}
error[E0106]: missing lifetime specifier --> /src/main_19.rs:3:11 | 3 | text: &str, | ^ expected lifetime parameter
Референции в структури
struct Words<'a> {
text: &'a str,
}
Референции в структури
Съответно трябва да добавим lifetime параметъра и към impl
блока
#[derive(Debug)]
struct Words<'a> {
text: &'a str,
}
impl<'a> Words<'a> {
fn new(text: &str) -> Words {
Words { text }
}
}
Референции в структури
Животът на структурата е ограничен до това колко живее обектът, от който сме взели референция
let t1 = Words::new("a b c"); // Words<'static>
let t2 = {
let s = String::from("мой таен низ"); // ---+- 'a
Words::new(s.as_str()) // |- Words<'a>
}; // ---+
println!("{:?}", t1);
println!("{:?}", t2);
error[E0597]: `s` does not live long enough --> /src/main_22.rs:16:16 | 16 | Words::new(s.as_str()) // |- Words<'a> | ^ borrowed value does not live long enough 17 | }; // ---+ | - `s` dropped here while still borrowed ... 21 | } | - borrowed value needs to live until here
Lifetime elision в impl блок
Как се попълват пропуснатите lifetimes за функцията new
?
impl<'a> Words<'a> {
fn new(text: &str) -> Words {
Words { text }
}
}
Lifetime elision в impl блок
Expanded:
impl<'a> Words<'a> {
fn new<'b>(text: &'b str) -> Words<'b> {
Words { text }
}
}
Lifetime elision в impl блок
Expanded:
impl<'a> Words<'a> {
fn new<'b>(text: &'b str) -> Words<'b> {
Words { text }
}
}
- aлгоритъмът не взима под внимание lifetime-а
'a
Lifetime elision в impl блок
Expanded:
impl<'a> Words<'a> {
fn new<'b>(text: &'b str) -> Words<'b> {
Words { text }
}
}
- 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/main_25.rs:7:9 | 6 | fn new(text: &str) -> Self { | ---- help: add explicit lifetime `'a` to the type of `text`: `&'a str` 7 | Words { text } | ^^^^^^^^^^^^^^ lifetime `'a` required
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/main_26.rs:7:9 | 7 | Words { text } | ^^^^^ | note: first, the lifetime cannot outlive the lifetime 'b as defined on the method body at 6:12... --> /src/main_26.rs:6:12 | 6 | fn new<'b>(text: &'b str) -> Words<'a> { | ^^ note: ...so that reference does not outlive borrowed content --> /src/main_26.rs:7:17 | 7 | Words { text } | ^^^^ note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 5:6... --> /src/main_26.rs:5:6 | 5 | impl<'a> Words<'a> { | ^^ = note: ...so that the expression is assignable: expected Words<'a> found Words<'_>
Lifetime elision в impl блок
Ако искаме да използваме Self
, правилния вариант е:
impl<'a> Words<'a> {
fn new(text: &'a str) -> Self {
Words { text }
}
}
Имплементация на next_word
метода
Live demo
Имплементация на next_word
метода
Финален код (с добавени Option
-и)
#[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!()
}
}
}
Имплементация на next_word
метода
Всичко работи, но дали имаме правилните lifetimes?
fn hello() -> &'static str {
let mut words = Words::new("hello world");
words.next_word().unwrap()
}
error[E0597]: `words` does not live long enough --> /src/main_29.rs:25:5 | 25 | words.next_word().unwrap() | ^^^^^ borrowed value does not live long enough 26 | } | - borrowed value only lives until here | = note: borrowed value must be valid for the static lifetime... warning: variable does not need to be mutable --> /src/main_29.rs:24:9 | 24 | let mut words = Words::new("hello world"); | ----^^^^^ | | | help: remove this `mut` | = note: #[warn(unused_mut)] on by default
Имплементация на 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!()
}
}
}
Имплементация на next_word
метода
Вместо това може да върнем резултат с lifetime-а на оригиналния низ в полето text
('a
).
impl<'a> Words<'a> {
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 hello() -> &'static str {
let mut words = Words::new("hello world");
words.next_word().unwrap()
}
fn main() {
println!("{}", hello());
}
hello
Lifetime bounds
Релацията "надживява"
Lifetime bounds
Релацията "надживява"
'a: 'b
Lifetime bounds
Релацията "надживява"
'a: 'b
'а ≥ 'b
Lifetime bounds
Релацията "надживява"
'a: 'b
'а ≥ 'b
T: 'b
Lifetime bounds
Релацията "надживява"
'a: 'b
'а ≥ 'b
T: 'b
T
живее поне колкото'b
Lifetime bounds
Релацията "надживява" за структури
- ако
T
е owning type (няма референции към други стойности), тоT: 'static
Lifetime bounds
Релацията "надживява" за структури
- ако
T
е owning type (няма референции към други стойности), тоT: 'static
u8: 'static
Lifetime bounds
Релацията "надживява" за структури
- ако
T
е owning type (няма референции към други стойности), тоT: 'static
u8: 'static
String: 'static
Lifetime bounds
Релацията "надживява" за структури
- ако
T
е owning type (няма референции към други стойности), тоT: 'static
u8: 'static
String: 'static
- ако
T
е референция&'a U
, или съдържа такава, тоT: 'a
Lifetime bounds
Релацията "надживява" за структури
- ако
T
е owning type (няма референции към други стойности), тоT: 'static
u8: 'static
String: 'static
- ако
T
е референция&'a U
, или съдържа такава, тоT: 'a
&'a str: 'a
Lifetime bounds
Релацията "надживява" за структури
- ако
T
е owning type (няма референции към други стойности), тоT: 'static
u8: 'static
String: 'static
- ако
T
е референция&'a U
, или съдържа такава, тоT: 'a
&'a str: 'a
&'static str: 'static
Lifetime bounds
Quirks
struct Foo<'a, T> {
r: &'a T,
}
error[E0309]: the parameter type `T` may not live long enough --> /src/main_32.rs:3:5 | 2 | struct Foo<'a, T> { | - help: consider adding an explicit lifetime bound `T: 'a`... 3 | r: &'a T, | ^^^^^^^^ | note: ...so that the reference type `&'a T` does not outlive the data it points at --> /src/main_32.rs:3:5 | 3 | r: &'a T, | ^^^^^^^^
Lifetime bounds
Quirks
struct Foo<'a, T: 'a> {
r: &'a T,
}
Дреболийки
Повече референции
- можем да имаме повече от една референция
struct TwoRefs<'a, 'b> {
text1: &'a str,
text2: &'b str,
}
Дреболийки
Повече референции
- структурата живее колкото най-малкия lifetime
- затова обикновено е безсмислено да има повече от един lifetime параметър за структура
struct TwoRefs<'a> {
text1: &'a str,
text2: &'a str,
}
Дреболийки
Lifetimes and generics
Aко ни се наложи да декларираме lifetimes и generics заедно, редът на декларация е lifetimes
преди generics
struct Words<T, 'a> {
text: &'a str,
count: T
}
error: lifetime parameters must be declared prior to type parameters --> /src/main_36.rs:2:17 | 2 | struct Words<T, 'a> { | ^^
Дреболийки
Lifetimes and generics
Aко ни се наложи да декларираме lifetimes и generics заедно, редът на декларация е lifetimes
преди generics
struct Words<'a, T> {
text: &'a str,
count: T
}