Common misconceptions about lifetimes in Rust

(translator's note: lifetimes is one of the most confusing things in Rust, which often causes difficulty for beginners, even despite the official documentation . There are explanations on individual aspects of lifetimes, but they are all scattered across different sources and answers on Stack Overflow. The author of this article has collected and clarified a lot of life-related issues in one place, which makes this article so valuable (and I learned new things from here myself). does not speak English enough to read the original fluently, and also to increase the popularity of this article among the Russian-speaking Rust community)



May 19, 2020 37 minutes #rust # lifetimes



Table of contents





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 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 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