关于Rust生命周期的常见误解

(译者注:生命周期是Rust中最令人困惑的事情之一,即使有官方文档也经常给初学者造成困难。对生命周期的各个方面都有解释,但它们都分散在不同的来源和答案中本文的作者在一个地方进行了收集,并阐明了许多与生活有关的问题,这使本文非常有价值(我从这里也学到了新知识),因此我决定将其翻译,以便那些的英语说得不够流利,无法流利地阅读原文,也无法提高本文在俄语Rust社区中的知名度)



2020年5月19日37分钟#rust#一生



目录





介绍



- , , . , .



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