Why does this "impl + 'static" code compile?

A type T is 'static if any value of that type is guaranteed no to have any dangling references or borrows even if it were to exist / be owned until the end of time.

  • This definition may look contrived, but is exactly what is required for soundness: if you want to run a closure in another thread, which may run it arbitrarily late, Rust will require that the closure be guaranteed not to use any dangling pointers (that would be unsound). A way of guaranteeing it is by guaranteeing the stronger property of not even posessing such dangling pointers, and a way of guaranteeing that a particular instance does not posess such dangling pointer is by guaranteeing that all the elements / instances of that type cannot posess such dangling pointer.

As a practical rule of thumb, you can ask yourself the question: could I store a value of that type in a static / global variable?

For instance, a String, in practice, almost always ends up dropped before the end of the program, so you may be tempted to think that a String cannot be 'static. Which is false: imagine the following program:

use ::spin::{ // 0.5.2
    Mutex,
};

static GLOBAL_VARIABLE: Mutex<Option<String>> = Mutex::new(None);

fn main ()
{
    let s = String::from("Hello, World!");
    *GLOBAL_VARIABLE.lock() = Some(s);
    // Now `s` lives in / is held by a global variable,
    // until the end of the program life.
}

So, long story short, all the "primitive" types are 'static.

There are only two counter examples to this:

  • Rust references (&'a T or &'a mut T), which may dangle after the lifetime of the borrow ('a)

    • Only a &'static T / &'static mut T gets to be 'static (and requires that T : 'static)
  • any struct or enum / newtype containing a Rust reference gets "infected" with the 'a lifetime parameter and bound.

Example

use ::spin::{ // 0.5.2
    Mutex,
};

static GLOBAL_VARIABLE: Mutex<Option<&str>> = Mutex::new(None);

fn main ()
{
    let s = String::from("Hello, World!");
    *GLOBAL_VARIABLE.lock() = Some(&s);
    // Error, `&s` dangles at some point,
    // so it cannot be stored in a forever-living global variable
}
  • yields:

    error[E0597]: `s` does not live long enough
      --> src/main.rs:10:36
       |
    10 |     *GLOBAL_VARIABLE.lock() = Some(&s);
       |      ----------------------        ^^ borrowed value does not live long enough
       |      |
       |      argument requires that `s` is borrowed for `'static`
    ...
    13 | }
       | - `s` dropped here while still borrowed
    

Rule of thumb

  • Any non-generic type is automatically 'static

  • Any generic type is 'static only when instanced with 'static lifetimes (for the generic lifetime parameters) and types that recursively are 'static (for the generic type parameters).

    • For instance, the type Ref<'a, T>
      /// Two generic parameters:
      ///
      ///   - a lifetime parameter `'a`,
      /// 
      ///   - and a type parameter `T`.
      struct Ref<'a, T> {
          field: &'a T,
      }
      
      only gets to be 'static if T : 'static (e.g., T = String) and 'a = 'static
4 Likes