What is the lifetime of a const variable?

In this tutorial page, it says:

  • const: An unchangeable value (the common case).
  • static: A possibly mutable variable with ['static] lifetime. The static lifetime is inferred and does not have to be specified. Accessing or modifying a mutable static variable is [unsafe].

It doesn't say what is the lifetime of const.

By commonsense, when you intentionally say something is static, that means other things are not static, so in this case I assume const's lifetime is not 'static.
Then what is its lifetime?

Thanks in advance!

const values get inserted into wherever they are used, so the const declaration itself doesn't have any associated lifetime. When you assign it to a variable, a new value is created, and that value is dropped if/when the variable is dropped.

const A: i32 = 5;
    let x = A;
    let y = &x;
} // y has an &'a i32 and 'a ends here

If you take a reference to a constant, it usually undergoes constant promotion, which essentially creates a hidden static to get a reference to.

const A: i32 = 5;
    let x = &A;
} // x has an &'static i32

If it doesn't get constant promotion by the rules linked above, it will get temporary lifetime extension (the next thing on the same page), which means it creates a hidden local variable that is dropped at the end of the current scope, similar to the first example.

const A: Cell<i32> = Cell::new(5); // Cell has interior mutability
    let x = &A;
} // x has an &'a i32 and 'a ends here

Any lifetimes within the constant are always 'static.

const A: &i32 = &5; // A is &'static i32

I'm not sure if this is technically constant promotion, temporary lifetime extension, or something else, but the result is you get a 'static lifetime either way.


Note that this is an actually bad example - if you need interior mutability, you need static or thread_local (which can be modified from one place and then read from another), not const.

This basically makes A a short way to write Cell::new(5). But the point is that this is a value that doesn't get const promoted. Nearly any value you put in a const that doesn't get const promoted is going to be a bad idea, not useful, or at least confusing.

1 Like

In this reply, I'm using the distinction between lifetimes ('_) and value liveness I outlined in your other topic.

Applying the distinction to the tutorial page: the static value doesn't have a lifetime, but it lasts "forever" (the run of your program). It's liveness scope is "forever". If the type of a static happens to have lifetimes, those lifetimes must be 'static.

Let's talk about Rust lifetimes ('_) first.

If the type of a const declared at the top level has lifetimes, those lifetimes are 'static. There's no other named lifetimes it could be, after all. However, you can also declare consts in other places, like associated constants of types. In those contexts, a 'static default for elided lifetimes doesn't apply, and there are may be other possible named lifetimes available (if the type or any traits involved have lifetime parameters).

Additionally, there was an accidental stabilization of allowing elided lifetimes in associated consts to be inferred at the use site (which is more general than being 'static, and thus more general than top-level consts). For now, eliding explicit lifetimes in a context other than the top level generates a warning. Some day it may once again be an error.

Examples. (And some invariance/inferred examples.)

...so any lifetimes in the types of a const declared at the top level are 'static, in associated consts they may or may not be 'static, or it might even be inferred at the use site and not enforced at the declaration site (for now).

Now let's talk about liveness scopes.

A const acts as-if if you pasted the definition everywhere you use the const value. So the liveness scope is going to depend on how you use the value. If the type of the const satisfies a 'static bound, you could perhaps[1] assign it to a static and the value will last forever. Or you could assign it to a local variable, and the liveness of that value will act like any other local variable.

@drewtato mentioned constant promotion -- if you use the const in such a way that the compiler decides it should use constant promotion to make the value a static value, naturally the value will last forever (and any lifetimes of the type will necessarily be 'static).

  1. static types must also be Sync, but const types don't have to be -- since there is no global value at the declaration site ↩︎

consts are neither variables nor some kind of "storage", meaning they don't have lifetimes. Instead, they are just an alias for a value, in kinda the same way that true is a way to name the "true" variant value of the bool type.

You may see the effect of rvalue static promotion when using them (which allows to upgrade references to some kind of constant expressions to 'static references, by implicitly putting them into static storage), but that's something separated from const declarations and in fact works for other types of expressions too (e.g. the true mentioned before).

Thanks for your explanation. I am still trying to understand.

const values get inserted into wherever they are used, so the const declaration itself doesn't have any associated lifetime.

Based on that, I understand why const A: i32 = 5; doesn't have a lifetime.
However, in your last example, const A: &i32 = &5; // A is &'static i32 where it has a lifetime.

I interpret it in the way that:

  1. when a constant item is a reference, like const A: &i32 = &5, it has a lifetime.
  2. otherwise, if its type is NOT a reference, then it doesn't have a lifetime.

I wonder if this is correct?

It's correct, but the most fundamental fact is that the only primitive types that have lifetimes are &_ and &mut _. This is true for any type, constant or not.

Here's a const with a lifetime ('static).

Here's another with a non-'static lifetime.[1]

It's semantics, but I'd probably say references aren't primitive types, or if they are, there are other primitive types with lifetimes (function pointers and trait objects, and perhaps even unnameable types like closures, function items, asyncs...).

The presence of a lifetime practically always comes down to[2] a reference being created somewhere. Perhaps that is the point being made.[3]

  1. when 'a is not 'static; technically there's an infinite number of consts from a type theory POV, one for any given lifetime ↩︎

  2. the possibility of ↩︎

  3. Even then, the reference itself need not stick around. ↩︎

So basically, any situations can happen to a const.

When it is a reference, it can have a lifetime.
Otherwise, it doesn't have a lifetime.

When it has a lifetime,

  1. when it is global, the lifetime is static
  2. when it is not global, the lifetime could be static or something else.

Hopefully I understood it correctly this time.

Non-reference consts can have lifetimes (as demonstrated).

When it comes to borrow checking, especially borrow checking within a function body, references are pretty special. Borrow checking knows how to reborrow through a reference without borrowing the reference itself, it knows how to propogate borrows through nested references, it knows that dereferencing a reference is "pure" and can perform borrow splitting (borrowing individual fields of a struct, say) through them... stuff like that.

But at the type level, like what can go into a static or const or how lifetime bounds work, references are a lot less special. A const Ref<'_, T> is pretty much the same as a const &T in that sense.

I am reading through the examples you provided above.
I really appreciate your time and help!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.