(nota do tradutor: vidas são uma das coisas mais confusas em Rust, o que muitas vezes causa dificuldade para iniciantes, mesmo apesar da documentação oficial . Existem explicações sobre aspectos individuais de vidas, mas todas estão espalhadas por diferentes fontes e respostas no Stack Overflow. O autor deste artigo reuniu em um só lugar e esclareceu muitas questões relacionadas à vida, o que torna este artigo tão valioso (aprendi coisas novas aqui sozinho). Decidi traduzi-lo para que possa ser lido por aqueles que não fala inglês o suficiente para ler o original com fluência e também para aumentar a popularidade deste artigo entre a comunidade Rust de língua russa)
19 de maio de 2020 37 minutos #rust # lifetimes
Índice
- Introdução
- Delírios
1) Tcontém apenas tipos proprietários2) T: 'static,Tdeve durar toda a duração do programa3) &'a TeT: 'a- eles são iguais- 4) meu código não é genérico e não tem vida útil
- 5) se ele compilar, então as anotações de minha vida estão corretas
- 6) objetos de traço por trás dos ponteiros de propriedade não têm vidas úteis
- 7) mensagens de erro de compilação dirão como consertar meu programa
- 8) os tempos de vida podem aumentar e diminuir no tempo de execução
- 9) enfraquecimento de links mut para links compartilhados com segurança
- 10) fechamentos seguem as mesmas regras para inferir implicitamente a elisão vitalícia como funções
11) 'staticlinks sempre podem levar a'a
- Conclusão
- Discussão
- Contatos
- Leitura adicional
Introdução
- , , . , .
T |
1)
2) |
, , , i32, String, Vec . . |
|
| 1)
2) |
, , &i32, &mut i32 . . |
| 1) mut-
2) |
, .. &mut T |
| 1) immut-
2) |
, .. &T |
: — , , , . ~6500 , .
1) T
, , Rust, , . :
Rust, , i32, &i32 &mut i32 — . , T , . , , , . , Rust :
T |
&T |
&mut T |
|
i32 |
&i32 |
&mut i32 |
T . &T . &mut T . T, &T &mut T — . , , , , . Rust -:
T |
&T |
&mut T |
|
i32, &i32, &mut i32, &&i32, &mut &mut i32, ... |
&i32, &&i32, &&mut i32, ... |
&mut i32, &mut &mut i32, &mut &i32, ... |
T, &T &mut T — , . T &T &mut T, &T &mut T — . , :
trait Trait {}
impl<T> Trait for T {}
impl<T> Trait for &T {} //
impl<T> Trait for &mut T {} //
, :
error[E0119]: conflicting implementations of trait `Trait` for type `&_`:
--> src/lib.rs:5:1
|
3 | impl<T> Trait for T {}
| ------------------- first implementation here
4 |
5 | impl<T> Trait for &T {}
| ^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&_`
error[E0119]: conflicting implementations of trait `Trait` for type `&mut _`:
--> src/lib.rs:7:1
|
3 | impl<T> Trait for T {}
| ------------------- first implementation here
...
7 | impl<T> Trait for &mut T {}
| ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&mut _`
Trait &T &mut T, Trait T, &T &mut T. , , &T &mut T :
trait Trait {}
impl<T> Trait for &T {} //
impl<T> Trait for &mut T {} //
T&T,&mut T&T&mut T
2) T: 'static, T
T: 'static«T'static»&'static TT: 'static—-
T: 'static,T -
T: 'static,T
Rust 'static, , :
fn main() {
let str_literal: &'static str = "str literal";
}
, "str literal" , , 'static. static static .
static BYTES: [u8; 3] = [1, 2, 3];
static mut MUT_BYTES: [u8; 3] = [1, 2, 3];
fn main() {
MUT_BYTES[0] = 99; // ,
unsafe {
MUT_BYTES[0] = 99;
assert_eq!(99, MUT_BYTES[0]);
}
}
static :
- ,
'static , , - static , ? , 'static , ?
, , , 'static, , 'static. , , .
&'static T T: 'static.
&'static T — T, , . , T . T . 'static , :
use rand;
// 'static str
fn rand_str_generator() -> &'static str {
let rand_string = rand::random::<u64>().to_string();
Box::leak(rand_string.into_boxed_str())
}
T: 'static — T, , . T: 'static &'static T, , String, Vec . . , , , , , . T: 'static , «T 'static», «T 'static». :
use rand;
fn drop_static<T: 'static>(t: T) {
std::mem::drop(t);
}
fn main() {
let mut strings: Vec<String> = Vec::new();
for _ in 0..10 {
if rand::random() {
//
//
let string = rand::random::<u64>().to_string();
strings.push(string);
}
}
// ,
// 'static
for mut string in strings {
//
string.push_str("a mutation");
//
drop_static(string); //
}
//
println!("i am the end of the program");
}
T: 'static, «T'static»-
T: 'static,T'static. -
T: 'static, ,T
3) &'a T T: 'a —
.
&'a T T: 'a, T, 'a, 'a, T 'a. , Rust &'static Ref<'a, T> Ref 'a, 'static .
T: 'a &'a T, .
// , 'a
fn t_ref<'a, T: 'a>(t: &'a T) {}
// , 'a
fn t_bound<'a, T: 'a>(t: T) {}
// ,
struct Ref<'a, T: 'a>(&'a T);
fn main() {
let string = String::from("string");
t_bound(&string); //
t_bound(Ref(&string)); //
t_bound(&Ref(&string)); //
t_ref(&string); //
t_ref(Ref(&string)); // , ,
t_ref(&Ref(&string)); //
// 'static, , , 'a
t_bound(string); //
}
T: 'a,&'a TT: 'a, , ,&'a T-
T: 'static,T: 'a,'static>='a'a
4)
- (lifetime elision), , Rust :
- -
- , (. : , , )
- , —
&self&mut self,self
, :
//
fn print(s: &str);
//
fn print<'a>(s: &'a str);
//
fn trim(s: &str) -> &str;
//
fn trim<'a>(s: &'a str) -> &'a str;
// , ,
// . .
fn get_str() -> &str;
//
fn get_str<'a>() -> &'a str; //
fn get_str() -> &'static str; // 'static
// , ,
// . .
fn overlap(s: &str, t: &str) -> &str;
//
// ( )
fn overlap<'a>(s: &'a str, t: &str) -> &'a str; // s
fn overlap<'a>(s: &str, t: &'a str) -> &'a str; // t
fn overlap<'a>(s: &'a str, t: &'a str) -> &'a str; // s t
fn overlap(s: &str, t: &str) -> &'static str; // s t
fn overlap<'a>(s: &str, t: &str) -> &'a str; //
//
fn overlap<'a, 'b>(s: &'a str, t: &'b str) -> &'a str;
fn overlap<'a, 'b>(s: &'a str, t: &'b str) -> &'b str;
fn overlap<'a>(s: &'a str, t: &'a str) -> &'a str;
fn overlap<'a, 'b>(s: &'a str, t: &'b str) -> &'static str;
fn overlap<'a, 'b, 'c>(s: &'a str, t: &'b str) -> &'c str;
//
fn compare(&self, s: &str) -> &str;
//
fn compare<'a, 'b>(&'a self, &'b str) -> &'a str;
- :
- ,
- ,
- - ( )
- ( )
.
- Rust ,
5) ,
- (borrow checker) Rust , ,
- Rust
Rust , . :
struct ByteIter<'a> {
remainder: &'a [u8]
}
impl<'a> ByteIter<'a> {
fn next(&mut self) -> Option<&u8> {
if self.remainder.is_empty() {
None
} else {
let byte = &self.remainder[0];
self.remainder = &self.remainder[1..];
Some(byte)
}
}
}
fn main() {
let mut bytes = ByteIter { remainder: b"1" };
assert_eq!(Some(&b'1'), bytes.next());
assert_eq!(None, bytes.next());
}
ByteIter — , . Iterator. , , , ?
fn main() {
let mut bytes = ByteIter { remainder: b"1123" };
let byte_1 = bytes.next();
let byte_2 = bytes.next();
if byte_1 == byte_2 {
// -
}
}
! :
error[E0499]: cannot borrow `bytes` as mutable more than once at a time
--> src/main.rs:20:18
|
19 | let byte_1 = bytes.next();
| ----- first mutable borrow occurs here
20 | let byte_2 = bytes.next();
| ^^^^^ second mutable borrow occurs here
21 | if byte_1 == byte_2 {
| ------ first borrow later used here
, . — , , ByteIter , &'a [T], , /. , , , , , , ?
, ! , . , :
struct ByteIter<'a> {
remainder: &'a [u8]
}
impl<'a> ByteIter<'a> {
fn next<'b>(&'b mut self) -> Option<&'b u8> {
if self.remainder.is_empty() {
None
} else {
let byte = &self.remainder[0];
self.remainder = &self.remainder[1..];
Some(byte)
}
}
}
. . , Rust: . :
struct ByteIter<'remainder> {
remainder: &'remainder [u8]
}
impl<'remainder> ByteIter<'remainder> {
fn next<'mut_self>(&'mut_self mut self) -> Option<&'mut_self u8> {
if self.remainder.is_empty() {
None
} else {
let byte = &self.remainder[0];
self.remainder = &self.remainder[1..];
Some(byte)
}
}
}
'mut_self, 'remainder! .
struct ByteIter<'remainder> {
remainder: &'remainder [u8]
}
impl<'remainder> ByteIter<'remainder> {
fn next(&mut self) -> Option<&'remainder u8> {
if self.remainder.is_empty() {
None
} else {
let byte = &self.remainder[0];
self.remainder = &self.remainder[1..];
Some(byte)
}
}
}
fn main() {
let mut bytes = ByteIter { remainder: b"1123" };
let byte_1 = bytes.next();
let byte_2 = bytes.next();
std::mem::drop(bytes); // !
if byte_1 == byte_2 { //
// -
}
}
, , , . Rust ? : (memory safe).
(borrow checker) Rust- , . Rust , - .
, : Rust , .
#[derive(Debug)]
struct NumRef<'a>(&'a i32);
impl<'a> NumRef<'a> {
// 'a,
// self 'a, ? (: , )
fn some_method(&'a mut self) {}
}
fn main() {
let mut num_ref = NumRef(&5);
num_ref.some_method(); // num_ref
num_ref.some_method(); //
println!("{:?}", num_ref); //
}
- , 'a, &'a mut self. Rust, « ». , Rust some_method, , , . , , , , . , Rust:
#[derive(Debug)]
struct NumRef<'a>(&'a i32);
impl<'a> NumRef<'a> {
// mut self 'a
fn some_method(&mut self) {}
//
fn some_method_desugared<'b>(&'b mut self){}
}
fn main() {
let mut num_ref = NumRef(&5);
num_ref.some_method();
num_ref.some_method(); //
println!("{:?}", num_ref); //
}
- Rust
- Rust ,
- ,
6) -
Rust . Rust -:
- - ́ , -
- , ,
- , , ,
- ,
- ,
-
'static,'static - , ,
'static
, « - ». , , , :
use std::cell::Ref;
trait Trait {}
//
type T1 = Box<dyn Trait>;
// , Box<T> T,
// 'static
type T2 = Box<dyn Trait + 'static>;
//
impl dyn Trait {}
//
impl dyn Trait + 'static {}
//
type T3<'a> = &'a dyn Trait;
// , &'a T T: 'a, 'a
type T4<'a> = &'a (dyn Trait + 'a);
//
type T5<'a> = Ref<'a, dyn Trait>;
// , Ref<'a, T> T: 'a, 'a
type T6<'a> = Ref<'a, dyn Trait + 'a>;
trait GenericTrait<'a>: 'a {}
//
type T7<'a> = Box<dyn GenericTrait<'a>>;
//
type T8<'a> = Box<dyn GenericTrait<'a> + 'a>;
//
impl<'a> dyn GenericTrait<'a> {}
//
impl<'a> dyn GenericTrait<'a> + 'a {}
, , , , , , - . , , , :
trait Trait {}
struct Struct {}
struct Ref<'a, T>(&'a T);
impl Trait for Struct {}
impl Trait for &Struct {} //
impl<'a, T> Trait for Ref<'a, T> {} // ,
, , , - . :
use std::fmt::Display;
fn dynamic_thread_print(t: Box<dyn Display + Send>) {
std::thread::spawn(move || {
println!("{}", t);
}).join();
}
fn static_thread_print<T: Display + Send>(t: T) {
std::thread::spawn(move || {
println!("{}", t);
}).join();
}
:
error[E0310]: the parameter type `T` may not live long enough
--> src/lib.rs:10:5
|
9 | fn static_thread_print<T: Display + Send>(t: T) {
| -- help: consider adding an explicit lifetime bound...: `T: 'static +`
10 | std::thread::spawn(move || {
| ^^^^^^^^^^^^^^^^^^
|
note: ...so that the type `[closure@src/lib.rs:10:24: 12:6 t:T]` will meet its required lifetime bounds
--> src/lib.rs:10:5
|
10 | std::thread::spawn(move || {
| ^^^^^^^^^^^^^^^^^^
, , . .
use std::fmt::Display;
fn dynamic_thread_print(t: Box<dyn Display + Send>) {
std::thread::spawn(move || {
println!("{}", t);
}).join();
}
fn static_thread_print<T: Display + Send + 'static>(t: T) {
std::thread::spawn(move || {
println!("{}", t);
}).join();
}
, . 'static T, — ? . , Rust 'static , 'static. Rust:
use std::fmt::Display;
fn dynamic_thread_print(t: Box<dyn Display + Send + 'static>) {
std::thread::spawn(move || {
println!("{}", t);
}).join();
}
fn static_thread_print<T: Display + Send + 'static>(t: T) {
std::thread::spawn(move || {
println!("{}", t);
}).join();
}
- -
7) ,
- -
- Rust
— , :
use std::fmt::Display;
fn box_displayable<T: Display>(t: T) -> Box<dyn Display> {
Box::new(t)
}
:
error[E0310]: the parameter type `T` may not live long enough
--> src/lib.rs:4:5
|
3 | fn box_displayable<T: Display>(t: T) -> Box<dyn Display> {
| -- help: consider adding an explicit lifetime bound...: `T: 'static +`
4 | Box::new(t)
| ^^^^^^^^^^^
|
note: ...so that the type `T` will meet its required lifetime bounds
--> src/lib.rs:4:5
|
4 | Box::new(t)
| ^^^^^^^^^^^
, , . , , , - 'static :
use std::fmt::Display;
fn box_displayable<T: Display + 'static>(t: T) -> Box<dyn Display> {
Box::new(t)
}
, … , ? , , , . , :
use std::fmt::Display;
fn box_displayable<'a, T: Display + 'a>(t: T) -> Box<dyn Display + 'a> {
Box::new(t)
}
, , ! ? , . , :
fn return_first(a: &str, b: &str) -> &str {
a
}
:
error[E0106]: missing lifetime specifier
--> src/lib.rs:1:38
|
1 | fn return_first(a: &str, b: &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 `a` or `b`
help: consider introducing a named lifetime parameter
|
1 | fn return_first<'a>(a: &'a str, b: &'a str) -> &'a str {
| ^^^^ ^^^^^^^ ^^^^^^^ ^^^
. , , . :
fn return_first<'a>(a: &'a str, b: &str) -> &'a str {
a
}
- -
- Rust ,
- Rust , , , , .
8)
- ,
- Rust
:
struct Has<'lifetime> {
lifetime: &'lifetime str,
}
fn main() {
let long = String::from("long");
let mut has = Has { lifetime: &long };
assert_eq!(has.lifetime, "long");
{
let short = String::from("short");
// ""
has.lifetime = &short;
assert_eq!(has.lifetime, "short");
// " " ( )
has.lifetime = &long;
assert_eq!(has.lifetime, "long");
// `short`
}
// , `short` ""
assert_eq!(has.lifetime, "long");
}
:
error[E0597]: `short` does not live long enough
--> src/main.rs:11:24
|
11 | has.lifetime = &short;
| ^^^^^^ borrowed value does not live long enough
...
15 | }
| - `short` dropped here while still borrowed
16 | assert_eq!(has.lifetime, "long");
| --------------------------------- borrow later used here
, :
struct Has<'lifetime> {
lifetime: &'lifetime str,
}
fn main() {
let long = String::from("long");
let mut has = Has { lifetime: &long };
assert_eq!(has.lifetime, "long");
//
if false {
let short = String::from("short");
// ""
has.lifetime = &short;
assert_eq!(has.lifetime, "short");
// " " ( )
has.lifetime = &long;
assert_eq!(has.lifetime, "long");
// `short`
}
// , `short` ""
assert_eq!(has.lifetime, "long");
}
, , , if-else match , . , . , .
- , -
- Rust , ,
9) mut-
- (re-borrowing)
mut- , , Rust mut- :
fn takes_shared_ref(n: &i32) {}
fn main() {
let mut a = 10;
takes_shared_ref(&mut a); //
takes_shared_ref(&*(&mut a)); //
}
, mut- , ? — , :
fn main() {
let mut a = 10;
let b: &i32 = &*(&mut a); //
let c: &i32 = &a;
dbg!(b, c); //
}
:
error[E0502]: cannot borrow `a` as immutable because it is also borrowed as mutable
--> src/main.rs:4:19
|
3 | let b: &i32 = &*(&mut a);
| -------- mutable borrow occurs here
4 | let c: &i32 = &a;
| ^^ immutable borrow occurs here
5 | dbg!(b, c);
| - mutable borrow later used here
, , . Rust , mut-? , mut- :
use std::sync::Mutex;
struct Struct {
mutex: Mutex<String>
}
impl Struct {
// self
fn get_string(&mut self) -> &str {
self.mutex.get_mut().unwrap()
}
fn mutate_string(&self) {
// Rust ,
//
// , get_string
*self.mutex.lock().unwrap() = "surprise!".to_owned();
}
}
fn main() {
let mut s = Struct {
mutex: Mutex::new("string".to_owned())
};
let str_ref = s.get_string(); //
s.mutate_string(); // str_ref ,
dbg!(str_ref); // ,
}
, mut- , , : , . , , . , , , . , Rust -. - , , :
// T T
fn some_function<T>(some_arg: &mut T) -> &T;
struct Struct;
impl Struct {
// self
fn some_method(&mut self) -> &Self;
// self T
fn other_method(&mut self) -> &T;
}
, Rust - , , :
use std::collections::HashMap;
type PlayerID = i32;
#[derive(Debug, Default)]
struct Player {
score: i32,
}
fn start_game(player_a: PlayerID, player_b: PlayerID, server: &mut HashMap<PlayerID, Player>) {
// ,
let player_a: &Player = server.entry(player_a).or_default();
let player_b: &Player = server.entry(player_b).or_default();
// -
dbg!(player_a, player_b); //
}
. or_default() &mut Player, &Player - . , , :
use std::collections::HashMap;
type PlayerID = i32;
#[derive(Debug, Default)]
struct Player {
score: i32,
}
fn start_game(player_a: PlayerID, player_b: PlayerID, server: &mut HashMap<PlayerID, Player>) {
// Player,
server.entry(player_a).or_default();
server.entry(player_b).or_default();
// , ,
let player_a = server.get(&player_a);
let player_b = server.get(&player_b);
// -
dbg!(player_a, player_b); //
}
, , — , .
(. : . , server : Player, . Player , . , , , . Rust , Player )
- mut- ,
- mut- ,
10) ,
, .
, , , , .
fn function(x: &i32) -> &i32 {
x
}
fn main() {
let closure = |x: &i32| x;
}
:
error: lifetime may not live long enough
--> src/main.rs:6:29
|
6 | let closure = |x: &i32| x;
| - - ^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is &'2 i32
| let's call the lifetime of this reference `'1`
:
//
fn function<'a>(x: &'a i32) -> &'a i32 {
x
}
fn main() {
//
let closure = for<'a, 'b> |x: &'a i32| -> &'b i32 { x };
// , Rust,
}
. , , , , . ? :
fn main() {
// -, , ,
let identity: dyn Fn(&i32) -> &i32 = |x: &i32| x;
// , -
let identity: Box<dyn Fn(&i32) -> &i32> = Box::new(|x: &i32| x);
// ,
let identity: &dyn Fn(&i32) -> &i32 = &|x: &i32| x;
// :)
let identity: &'static (dyn for<'a> Fn(&'a i32) -> &'a i32 + 'static) = &|x: &i32| -> &i32 { x };
// ,
let identity: impl Fn(&i32) -> &i32 = |x: &i32| x;
// ,
let identity = for<'a> |x: &'a i32| -> &'a i32 { x };
// "impl trait"
fn return_identity() -> impl Fn(&i32) -> &i32 {
|x| x
}
let identity = return_identity();
//
fn annotate<T, F>(f: F) -> F where F: Fn(&T) -> &T {
f
}
let identity = annotate(|x: &i32| x);
}
, , , .
- , .
11) 'static- 'a-
:
fn get_str<'a>() -> &'a str; //
fn get_str() -> &'static str; // 'static
, , - . , , , , , .
, , 'static- , 'a-, Rust 'static- 'a- . : , , . , , .
use rand;
fn generic_str_fn<'a>() -> &'a str {
"str"
}
fn static_str_fn() -> &'static str {
"str"
}
fn a_or_b<T>(a: T, b: T) -> T {
if rand::random() {
a
} else {
b
}
}
fn main() {
let some_string = "string".to_owned();
let some_str = &some_string[..];
let str_ref = a_or_b(some_str, generic_str_fn()); //
let str_ref = a_or_b(some_str, static_str_fn()); //
}
, , :
use rand;
fn generic_str_fn<'a>() -> &'a str {
"str"
}
fn static_str_fn() -> &'static str {
"str"
}
fn a_or_b_fn<T, F>(a: T, b_fn: F) -> T
where F: Fn() -> T
{
if rand::random() {
a
} else {
b_fn()
}
}
fn main() {
let some_string = "string".to_owned();
let some_str = &some_string[..];
let str_ref = a_or_b_fn(some_str, generic_str_fn); //
let str_ref = a_or_b_fn(some_str, static_str_fn); //
}
:
error[E0597]: `some_string` does not live long enough
--> src/main.rs:23:21
|
23 | let some_str = &some_string[..];
| ^^^^^^^^^^^ borrowed value does not live long enough
...
25 | let str_ref = a_or_b_fn(some_str, static_str_fn);
| ---------------------------------- argument requires that `some_string` is borrowed for `'static`
26 | }
| - `some_string` dropped here while still borrowed
— , &'static str &'a str, for<T> Fn() -> &'static T for<'a, T> Fn() -> &'a T. — , — .
-
for<'a, T> fn() -> &'a T,for<T> fn() -> &'static T
T&T,&mut T&T&mut TT: 'static, «T'static»-
T: 'static,T'static. -
T: 'static, ,T
- -
T: 'a,&'a TT: 'a, , ,&'a T-
T: 'static,T: 'a,'static>='a'a - Rust ,
- Rust
- Rust ,
- ,
- -
- Rust , , , , .
- , -
- Rust , ,
- ,
- mut- ,
-
for<'a, T> fn() -> &'a T,for<T> fn() -> &'static T
- pretzelhammer Twitter
- (
WatchReleases only)
Rust, nlinker.