I noticed that the following code compiles even though it seems like it shouldn't. There is a method, add(), that requires a 'static parameter. The main function calls that method with a parameter that doesn't appear to have a static lifetime. What's going on?
According to the docs, there are only 2 ways to create objects with a static lifetime: make a constant with the static declaration, and Make a string literal which has type: &'static str. This is doing neither. See: Static - Rust By Example
The argument is passed by value, to be owned by the callee, and it doesn't contain any borrows that would limit its lifetime, so that makes it 'static. The callee could hold that value as long as it wants, including things like writing it to a global.
Would it make sense for me to suggest a change to the Rust By Example book? The book says there are exactly two ways to make a static variable, but this seems to be a third way. There may be more ways still.
I would agree there's some nuance missing in that documentation.
Really, any type that lacks a generic lifetime parameter will satisfy a 'static constraint. Your MyApp is a trivial example, but also things like Option<Foo> and Vec<Bar> can be 'static. But once you borrow something, then you have a real limiting lifetime.
struct MyStr<'a> {
s: &'a str,
}
Now only MyStr<'static> would itself be 'static. Also, it used to be the case that you could omit writing such lifetimes when they can be inferred, like fn new(s: &str) -> MyStr, but now this is a warning and you should write MyStr<'_> for inference. This way we can always see where lifetimes are involved.
Back to those docs, I think the intent is to describe ways to get a 'static reference to such objects. From static NUM: i32, you can get a &'static i32 to pass elsewhere, and string literals are always &'static str. But you can not take a &'static reference to your app argument unless you store it somewhere that will live for the rest of the program. One way to do that is with Box::leak, leaving it on the heap forevermore.
A typeT 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
}
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
Thanks for the thorough treatment of this topic, @cuviper and @Yandros! My understanding now is that 'static is essentially an auto-trait that applies automatically to most types. 'static variables don't necessarily live forever; the 'static lifetime just means that the variable is capable of being used as a global.
I humbly suggest that you two should submit a PR for this page: static - Rust