Const vs static

What are some examples where it is preferred to use a non-mut static variable instead of a const? I don't currently understand the need for Rust to support both, but I do understand why one might want a mut static variable.

A nice example is a static Lazy<Regex> which will compile the Regex only once in the entire duration of the program, while a const one would recompile it each time it is used.

3 Likes

Using a const variable is equivalent to copy-pasting its value everywhere it is used. A static variable is given a fixed location is memory, and each use is a reference to that location.

Even when using a non-mut static, you can still modify it if you are using a special type such as Lazy that supports modifying it in a safe way.

9 Likes

a pub static is accessible from C over FFI a const not.

and if the variable is big and used in many places it will bloat your binary since const are always inlined

3 Likes

Is const is preferred over static for small values like numbers that do not need lazy initialization and will not be accessed from C code?

What would be lost if Rust didn't have the const keyword and we just always used static in its place?

it makes a difference for types with do not implement Copy

struct NonCopy(i32);

const FOO: NonCopy = NonCopy(42);
static BAR: NonCopy = NonCopy(-1);

fn main() {
    let _works = FOO; // <-- Works using const is just like copy past and will insert `NonCopy(42)` here
    let _fails = BAR; // <-- Don't works variable don't implaments Copy
}

You can't have generic statics while generic const is possible. While the const is a compile time only thing, the static requires actual memory location which can be accessed globally. Usually this location is allocated by the compilation unit which declares the static, but it's not possible to allocate it for every possible generic types which can be near infinite.

4 Likes

Here's something I've wondered about: if I have a big constant array of numbers for use in some algorithm, will declaring that static be good for memory locality (the array only lives in one place, so it can be cached effectively), or is const good enough? And are there any downsides to using const in this situation?

If you make it const, you'll get a new copy of the array everywhere it is used. That could mean allocating a big chunk of stack space in every function that uses it, (and also potentially copying it around a bunch.) It can make a difference, but it's hard to say how much without some concrete numbers and a good understanding of what the optimizer is going to do.

const ARR: [i32; 5] = [1, 2, 3, 4, 5];

let x = &ARR[2];
let y = &ARR[3];
let z = &ARR[4]; // Each of these variables is pointing at a different array. That means we have to allocate 15 `u32` values somewhere, up to optimizations.

You would not typically use const for a big array that you wanted to reference in multiple places. You might use it for a small one, (such as const ORIGIN: [f32; 3] = [0.0, 0.0, 0.0]), because const is much better for things that are cheap to construct and impl Copy.

@skysch note that while you're technically right, the rust compiler promotes constant rvalues (with come exceptions) to static variables, so in practice ARR is always a single static variable, even in debug mode.

3 Likes

My point would then be: if that's the semantics you want to rely upon, you really should just declare ARR to be a static in the first place. You certainly wouldn't want some wacky edge-case to do something different while you're implicitly relying on pointer equality in some obscure fashion. So while they're equivalent when they can be, they are not intended to be equivalent.

2 Likes

Also, if you're always using the array by reference, you can do:

const ARR: &'static [i32; 5] = &[1, 2, 3, 4, 5];

Now whenever you use ARR, it is a reference to static memory, and always to the same static memory. (Note, you can elide the lifetime here, and 'static will be inferred.)

7 Likes

if you use a const slice only the reference is const but the backing data is static

const ARR: &[i32] = &[1, 2, 3, 4, 5]; // Using ARR will create a copy of the reference to the same data

but since it is a slice the reference will be a fat pointer

2 Likes

They will actually still be duplicated for each codegen unit:

5 Likes