Idées fausses courantes sur la durée de vie à Rust

(Note du traducteur: les durées de vie sont l'une des choses les plus déroutantes dans Rust, ce qui pose souvent des problèmes aux débutants, même en dépit de la documentation officielle . Il existe des explications sur certains aspects de la vie, mais elles sont toutes dispersées à travers différentes sources et réponses sur Stack Overflow. L'auteur de cet article a rassemblé en un seul endroit et clarifié de nombreux problèmes liés à la vie, ce qui rend cet article si précieux (et j'ai appris de nouvelles choses d'ici moi-même). J'ai décidé de le traduire pour qu'il puisse être lu par ceux qui ne parle pas assez anglais pour lire l'original couramment, et aussi pour augmenter la popularité de cet article parmi la communauté russophone de Rust)



19 mai 2020 37 minutes #rust # lifetimes



Table des matières





introduction



- , , . , .



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 T T: '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 TT, , . , 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: 'staticT, , . 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 T
  • T: '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 T
  • T: 'static , « T 'static»
  • T: 'static, T 'static .
  • T: 'static , , T

    • -
  • T: 'a , &'a T
  • T: '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


















Rust, nlinker.




All Articles