That's sound advice, but…
it means precisely that. If you write T : 'static
then means precisely and exactly that.
The key insight is here:
If you put lifetime (the runtime concept) in type (static, compile-time concept) then you are half-way on the road to the dependent typing.
Any type that's T: 'static
is a single, concrete type, be it i32
or f64
or even String
: variables of all these types are all identical and can be moved willy-nilly.
But any type that's not T: 'static
is “dynamic” type! Since type &'a str
doesn't exist!
Instead your program includes infinite number of such types.
One may even imagine some kind of research language where such types actually exist at runtime, are created “on the fly” and can be probed and processed using runtime reflection.
But Rust doesn't want that! Instead it postulates that we have to disallow any and all constructs where it's not possible to generate one, single, sequence of machine instructions that process all these different types simultaneously.
The closest analogue is type erasure in Java.
What's misleading are attempts to reduce number of lifetimes in discussions about your program and they cry that everything is, suddenly, incorrect because different things all have the same lifetime… but the confusion here is simply the result of that oversimplification!
In reality lifetimes are not too complicated if you just accept that everything in Rust can have lifetime attached: types, variables, variables contents, loans, etc.
'static str
doesn't make any sense. But str: 'static
makes sense (and is, in fact, needed to be able to say &'static str
).
Basically: any entity with nonfixed lifetime may only exist as long as another entity (that said entity refers) exist… but it can live for a shorter time!
Consider somewhat more complicated code:
fn foo<'a>(x: &'a i32) -> &'a i32{
let y;
{
// println!("{y}") – incorrect because lifetime of `y` and lifetime 'b are different.
let z = &x;
y = bar(z);
}
return y;
}
fn bar<'a, 'b>(m: &'a &'b i32) -> &'b i32 {
return *m;
}
Here you can see that lifetime of variable (x
or y
) is different from lifetime of value that's in that in that variable and it's different from lifetime of type and two named lifetimes ('a
and 'b
are different).
Normally we talk about lifetimes of loans, because we only care about loans, but in reality everything has a lifetime. And if you don't try to postulate that variable (== place in memory) should live for the same time as data in that variable (it may live for a longer or shorter time than place in memory!) and then understand that type of reference carriers intersection of two (reference is only valid as both place where it points to and it's content are both valid), but then reference is a variable, too and that means that place where you put that reference have a lifetime, too.
When we postulated that “all lifetime should be erased after compilation” we gave themselves a carte blanche to put as many distinct lifetimes as needed… and thus everything got a lifetime.
So it's not that “variables don't have lifetimes” in Rust, but more of “not just variables have lifetimes”… and since to actually use variable for anything you have to ensure that both variable and value in it are “alive”… we usually talk about lifetime of loans.
These lifetimes are the most useful… but that doesn't mean others don't exist!