Внимание: това ще е странно
{
let r;
{
let x = 5;
r = &x;
}
println!("r: {}", r);
}
error: `x` does not live long enough | 6 | r = &x; | - borrow occurs here 7 | } | ^ `x` dropped here while still borrowed ... 10 | } | - borrowed value needs to live until here
{
let r; // -------+-- 'a
// |
{ // |
let x = 5; // -+-----+-- 'b
r = &x; // | |
} // -+ |
// |
println!("r: {}", r); // |
// |
} // -------+
{
let x = 5; // -----+-- 'b
// |
let r = &x; // --+--+-- 'a
// | |
println!("r: {}", r); // | |
// --+ |
} // -----+
fn foo() {
let mut data = vec!['a', 'b', 'c']; // --+ 'scope
// |
// |
capitalize(&mut data[..]); // |
// ^~~~~~~~~~~~~~~~~~~~~~~~~ 'lifetime // |
data.push('d'); // |
data.push('e'); // |
data.push('f'); // |
} // <---------------------------------------+
fn capitalize(data: &mut [char]) {
// do something
}
fn foo() {
let mut data = vec!['a', 'b', 'c']; // --+ 'scope
// |
let slice = &mut data[..]; // --| 'lifetime
capitalize(slice); // |
// |
data.push('d'); // комп. грешка // |
data.push('e'); // комп. грешка // |
data.push('f'); // комп. грешка // |
} // <---------------------------------------+
fn capitalize(data: &mut [char]) {
// do something
}
fn longest(x: &str, y: &str) -> String {
if x.len() > y.len() {
String::from(x)
} else {
String::from(y)
}
}
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(&string1, string2);
println!("По-дългия низ е {}", result);
}
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(&string1, string2);
println!("По-дългия низ е {}", result);
}
error[E0106]: missing lifetime specifier | 1 | 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 longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(&string1, string2);
println!("По-дългия низ е {}", result);
}
fn main() {
let string1 = String::from("long string is long");
{
let string2 = String::from("xyz");
let result = longest(&string1, &string2);
println!("The longest string is {}", result);
}
}
fn main() {
let string1 = String::from("long string is long");
let result;
{
let string2 = String::from("xyz");
result = longest(&string1, &string2);
}
println!("The longest string is {}", result);
}
error: `string2` does not live long enough | 6 | result = longest(&string1, &string2); | ------- borrow occurs here 7 | } | ^ `string2` dropped here while still borrowed 8 | println!("The longest string is {}", result); 9 | } | - borrowed value needs to live until here
Но ние вече сме виждали функции, които връщат референция
fn first_word(s: &str) -> &str {
// код
}
Защо тогава работеше, а сега не?
Горната дефиниция е еквивалентна на:
fn first_word<'a> (s: &'a str) -> &'a str {
// код
}
Тяло на функция
Дефиниция на функция
Структура
За всеки пропуснат 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 параметър (експлицитен или пропуснат), този 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
Ако първият аргумент е &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-а.
fn get_str() -> &str; // комп. грешка
fn longest(x: &str, y: &str) -> &str; // комп. грешка
Специалният lifetime 'static
let s: &'static str = "I have a static lifetime.";
#define
, но по-добро
'static
lifetime, защото се inline-ватconst EULER_NUMBER: f32 = 2.71828;
const HI: &str = "hi"; // &'static str
const HI: String = String::from("hi"); // ??
fn main() {
const N: i32 = 7;
println!("{}", EULER_NUMBER);
}
struct Foo;
impl Foo {
const N: i32 = 7;
}
fn main() {
println!("{}", Foo::N);
}
const
const
предразполага за оптимизацииstatic NAME: &str = "Steve";
static NAME: &'static str = "Steve";
Можете да направите статичната променлива да е mutable, но има недостатъци
static mut N: i32 = 7;
Тъй като може да имате различни нишки които четат или пишат от променливата
по едно и също време, за да четете или пишете към нея, трябва да използвате
unsafe
блок.
fn f() {
static mut N: i32 = 7;
unsafe {
N += 1;
println!("{}", N);
}
}
for i in 0..3 {
f(); // ??
}
struct TextInfo {
text: String,
}
impl TextInfo {
fn new(text: &str) -> TextInfo {
TextInfo {
text: String::from(text)
}
}
}
Можем ли да избегнем копирането на низа?
Можем да пробваме със &'static str
struct TextInfo {
text: &'static str,
}
impl TextInfo {
fn new(text: &str) -> TextInfo {
TextInfo { text }
}
}
TextInfo::new("баба"); // ?
let s = String::from("дядо");
TextInfo::new(&s); // ?
Всъщност не искаме 'static
, искаме произволен lifetime
struct TextInfo {
text: &'a str,
}
error[E0261]: use of undeclared lifetime name `'a` --> src/main.rs:3:16 | 3 | text: &'a str, | ^^ undeclared lifetime
Трябва да си дефинираме lifetime-а като generic parameter
struct TextInfo<'a> {
text: &'a str,
}
Трябва да си дефинираме lifetime-а като generic parameter
struct TextInfo<'a> {
text: &'a str,
}
Съответно трябва да го добавим и на impl
блока
impl<'a> TextInfo<'a> {
fn new(text: &str) -> TextInfo {
TextInfo { text }
}
}
Сега вече всичко работи
fn main() {
let t1 = TextInfo::new("баба"); // TextInfo<'static>
let s = String::from("дядо"); // ---+- 'a
let t2 = TextInfo::new(&s); // |- TextInfo<'a>
// |
} // ---+
Животът на структурата е ограничен до това колко живее обектът, от който сме взели референция
let info;
{
let s = String::from("мой таен низ"); // ---+- 'a
info = TextInfo::new(&s); // |- TextInfo<'a>
} // ---+
println!("{:?}", info);
Как се попълват пропуснатите lifetimes за функцията new
?
impl<'a> TextInfo<'a> {
fn new(text: &str) -> TextInfo {
}
}
Expanded
impl<'a> TextInfo<'a> {
fn new<'b>(text: &'b str) -> TextInfo<'b> {
}
}
'a
Какво става ако имаме повече от една референция?
struct TwoRefs<'a, 'b> {
text1: &'a str,
text2: &'b str,
}
let r;
{
let second = String::from("second");
r = TwoRefs { text1: "first", text2: &second };
println!("{}", r.text2); // ?
}
println!("{}", r.text1); // ?
Структурата живее колкото най-малкия lifetime
Затова обикновено е безсмислено да има повече от един lifetime параметър за структура
struct TwoRefs<'a> {
text1: &'a str,
text2: &'a str,
}
let second = String::from("second");
let r = TwoRefs {
text1: "first", // автоматично конвертиране &'static str -> &'a str
text2: &second, // за да съвпадне с lifetime-а на second
};
Компилаторът автоматично конвертира от по-голям към по-малък lifetime
Не на последно място, ако ни се наложи да декларираме lifetimes и generics заедно,
редът на декларация е lifetimes
преди generics
struct TextInfo<'a, T> {
text: &'a str,
count: T
}
// Компилационна грешка
struct TextInfo<T, 'a> {
text: &'a str,
count: T
}