Constant evaluation in Rust allows for some computations or initializations to be performed at compile time rather than at runtime.
const PI: f64 = 3.1415;
static mut GLOBAL_COUNTER: i32 = 0;
const fn is a function whose result can be used to assign the value of const or static. Such functions can be called in a constant context.
const fn factorial(n: usize) -> usize { if n == 0 { 1 } else { n * factorial(n - 1) } } const FACT_5: usize = factorial(5); // Compiles!
Limitations of const fn:
Box::new etc.),Question: Can any function be used in a const context if its result does not change? For example, like this:
fn add(a: i32, b: i32) -> i32 { a + b } const RES: i32 = add(1, 2);
Typical wrong answer: Yes, since the function is pure and the result is known in advance.
Correct answer: No, the function must be explicitly declared as const fn, only then can it be called within const initialization. Regular functions are only called at runtime!
Example:
const fn add(a: i32, b: i32) -> i32 { a + b } const RES: i32 = add(1, 2); // Compiles!
Story
In a project dealing with computations of three-dimensional geometry, a developer tried to declare a lookup table using the result of a call to a regular function instead of a const fn. As a result, compilation errors occurred and the profit from compile-time evaluation was lost.
Story
Using static mut for a global cache led to data races when accessed from multiple threads (static mut is not safe!). Atomic or Mutex should have been used to synchronize access to the global resource.
Story
In an attempt to speed up the initialization of large arrays, they were defined as static, but forgot that static always has a fixed address, which caused the data not to be cached by the processor as local, and operations slowed down on the hot path of server logic. Local let-expressions with computations at runtime should have been used.