What is the difference between const and static?
Short answer: a const is a value the compiler copies into every place you use the name, so there is no
single address behind it. A static is one allocation for the whole program, and every reference to it
points at that same place. Use const for values, and static when you actually need that one address.
Neither one is a let
Section titled “Neither one is a let”Both take a type, and both can sit at the top of the file:
const MAX_RETRIES: u32 = 3;static GREETING: &str = "hello";
fn main() { println!("{MAX_RETRIES} retries, then {GREETING}");}3 retries, then helloThe difference is about memory, not syntax: whether one place exists behind the name.
A const is copied into each use
Section titled “A const is copied into each use”Give both of them a type that can change and the two names stop behaving alike:
use std::cell::Cell;use std::sync::atomic::{AtomicU32, Ordering};
const COUNTER: Cell<u32> = Cell::new(0);static SHARED: AtomicU32 = AtomicU32::new(0);
fn main() { COUNTER.set(1); println!("const: {}", COUNTER.get());
SHARED.store(1, Ordering::SeqCst); println!("static: {}", SHARED.load(Ordering::SeqCst));}const: 0static: 1Both statements write 1. The static reads it back; the const still reports 0. There was never one
COUNTER to write into — each mention of the name is a fresh copy of Cell::new(0), so set filled one
copy in and get read a second. The Reference puts the rule in one sentence: constants “are essentially
inlined wherever they are used”, and a constant is “not associated with a specific memory location in the
program”.
Cell is interior mutability’s business, not this page’s. It’s here because it turns “did that write land
somewhere I can read back?” into something you can answer by running the program.
A static is one allocation, so it isn’t a value
Section titled “A static is one allocation, so it isn’t a value”One allocation means a location rather than a value, and a location can’t be moved:
static LOG: Vec<String> = Vec::new();
fn main() { let log = LOG; println!("{}", log.len());}cargo build prints:
error[E0507]: cannot move out of static item `LOG` --> src/main.rs:4:15 |4 | let log = LOG; | ^^^ move occurs because `LOG` has type `Vec<String>`, which does not implement the `Copy` trait |help: consider borrowing here |4 | let log = &LOG; | +help: consider cloning the value if the performance cost is acceptable |4 | let log = LOG.clone(); | ++++++++
For more information about this error, try `rustc --explain E0507`.That one allocation is reachable from anywhere in the program, which is why its type has to be safe to share:
use std::cell::Cell;
static COUNTER: Cell<u32> = Cell::new(0);
fn main() { COUNTER.set(1); println!("{}", COUNTER.get());}error[E0277]: `Cell<u32>` cannot be shared between threads safely --> src/main.rs:3:17 |3 | static COUNTER: Cell<u32> = Cell::new(0); | ^^^^^^^^^ `Cell<u32>` cannot be shared between threads safely | = help: the trait `Sync` is not implemented for `Cell<u32>` = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU32` instead = note: shared static variables must have a type that implements `Sync`
For more information about this error, try `rustc --explain E0277`.The last note names the fix for global state you want to change: an atomic is a static too, with the same
single address, just safe to write to.
Which one
Section titled “Which one”The Reference’s list of reasons to prefer a static over a const is short — you’re storing a lot of
data, you need the single-address property, or you need interior mutability. Otherwise reach for const.
- A number, a string, a table of values:
const. Nothing shares it and nothing writes to it. - A large table, or anything you need a reference to:
static. One copy exists, instead of one per use site.
Compile time isn’t the dividing line either. static WIDTH: usize = 8; followed by let buf = [0u8; WIDTH];
compiles, and buf has eight elements, because a static’s initializer is evaluated at compile time. Memory
identity is the difference, not when the value is computed.
Related
Section titled “Related”- Why does Rust require the mut keyword? — immutability by default, for a binding
- Why would you shadow a variable instead of using mut? — the article before this one
- Why does Rust move values instead of copying them? — what “cannot move out of” is about
- The bits that trip everyone up — the rest of this chapter
- Next in this chapter: how a function returns a value — the missing semicolon that throws it away