(Anmerkung des Übersetzers: Die Lebensdauer ist eines der verwirrendsten Dinge in Rust, was Anfängern trotz der offiziellen Dokumentation häufig Schwierigkeiten bereitet . Es gibt Erklärungen zu einzelnen Aspekten der Lebensdauer, die jedoch alle über verschiedene Quellen und Antworten verteilt sind Der Autor dieses Artikels hat an einem Ort viele lebensbezogene Probleme gesammelt und geklärt, was diesen Artikel so wertvoll macht (ich habe hier selbst neue Dinge gelernt). Ich habe beschlossen, ihn zu übersetzen, damit er von denen gelesen werden kann, die ihn lesen spricht nicht genug Englisch, um das Original fließend zu lesen und die Popularität dieses Artikels in der russischsprachigen Rust-Community zu steigern.
19. Mai 2020 37 Minuten #Rust # Lebensdauern
Inhaltsverzeichnis
- Einführung
- Wahnvorstellungen
1) Tenthält nur Besitzertypen2) T: 'static, dannTsollte es für die gesamte Dauer des Programms leben3) &'a TundT: 'a- sie sind gleich- 4) Mein Code ist nicht generisch und hat keine Lebensdauer
- 5) Wenn es kompiliert wird, sind meine lebenslangen Anmerkungen korrekt
- 6) Merkmalsobjekte hinter den besitzenden Zeigern haben keine Lebensdauer
- 7) Kompilierungsfehlermeldungen zeigen mir, wie ich mein Programm reparieren kann
- 8) Die Lebensdauern können zur Laufzeit hoch und runter gehen
- 9) Mut-Links zu gemeinsam genutzten Links sicher schwächen
- 10) Verschlüsse folgen den gleichen Regeln, um implizit auf die Elision der Lebensdauer als Funktionen zu schließen
11) 'staticLinks können immer zu führen'a
- Fazit
- Diskussion
- Kontakte
- Weiterführende Literatur
Einführung
- , , . , .
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.