Why does `const` allow referencing some `static` variables but not others (e.g., `AtomicBool`)?

Why does const allow referencing some static variables but not others (e.g., AtomicBool)?

I encountered an issue where referencing a static variable of type AtomicBool in a const results in a compilation error, but referencing other types (e.g., a custom struct) works fine. Here's a minimal example:

Code with AtomicBool (fails to compile):

use std::sync::atomic::AtomicBool;

static __A__FLAG__: AtomicBool = AtomicBool::new(false);

const FLAG: &'static AtomicBool = &__A__FLAG__; // Error: constructing invalid value

pub fn main() {
    println!("{:p}", FLAG);
}

Playground link

The error message is:

error[E0080]: it is undefined behavior to use this value
  --> src/main.rs:5:1
   |
5 | const FLAG: &'static AtomicBool = &__A__FLAG__;
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered reference to mutable memory in `const`

Code with a custom struct (compiles successfully):

struct A(bool);

impl A {
    const fn new() -> Self {
        Self(false)
    }
}

static __A__FLAG2__: A = A::new();

const FLAG2: &'static A = &__A__FLAG2__; // Works fine

pub fn main() {
    println!("{:p}", FLAG2);
}

Observations:

  1. Referencing a static variable of type AtomicBool in a const is not allowed.
  2. Referencing a static variable of a custom struct (without interior mutability) in a const works fine.

Questions:

  1. Why does Rust allow referencing some static variables in const but not others (e.g., AtomicBool)?
  2. Is this behavior intentional, or is it a limitation of the current compiler implementation?
  3. If intentional, what is the rationale behind disallowing references to static variables with interior mutability (like AtomicBool) in const?

I understand that const requires values to be fully determined at compile time, but the address of a static variable is fixed at runtime. Why can't const simply store the address of a static variable, even if its value might change?

Any insights or clarifications would be greatly appreciated!

Freeze requirement

Const evaluation is not allowed to

  • access the contents of any mutable static (whether that via interior mutability or static mut).
  • result in values that safely reference anything mutable (whether that is via interior mutability or &mut).
1 Like

I don't know the reason why references are not allowed, but raw pointer works fine if you just need the address.

-const FLAG: &'static AtomicBool = &__A__FLAG__;
+const FLAG: *const AtomicBool = &__A__FLAG__;
2 Likes

I think the most up-to-date statement of the boundary here is in the the tracking issue for (now stabilized) const_refs_to_static:

This feature allow constants to refer to statics. However, reading from a static that is mutable (static mut, or static with interior mutability) leads to an evaluator error. This is done to ensure that it doesn't matter when the constant is evaluated, it will always produce the same result.

Having a reference to a mutable static in the final value leads to a validation error. This is necessary to ensure that converting these references to a valtree (e.g. for pattern matching) will not error. (The conversion would error because valtree conversion must never read from anything mutable.)

This seems to me like a restriction that could be further relaxed by allowing references to types that aren’t StructuralPartialEq and therefore cannot participate in the things that use valtrees — but that would then mean that changing a type such that it either starts or stops implementing StructuralPartialEq is a breaking change.

1 Like