misnomer___
misnomer___

Reputation: 405

Why are const atomic variables not updated, but static atomic variables are?

I have this code:

use std::sync::atomic::{AtomicUsize, Ordering};

const SOME_VAR: AtomicUsize = AtomicUsize::new(0);

fn main() {
    println!("{}", SOME_VAR.load(Ordering::SeqCst));
    println!("{}", SOME_VAR.fetch_add(10, Ordering::SeqCst));
    println!("{}", SOME_VAR.load(Ordering::SeqCst));
}

This prints 0 0 0 without any errors. In Java, I can use a final HashMap and add (k, v) to it. In Rust, I am surprised that the compiler is not yelling at me but also does not increment my atomic value. Am I doing something wrong here?

If I use a static:

static SOME_VAR: AtomicUsize = AtomicUsize::new(0);

I get the result 0 0 10. Why does it not work with const?

Upvotes: 18

Views: 5506

Answers (2)

Bryan Larsen
Bryan Larsen

Reputation: 10016

For those who, like me, got here via Google because they got an error trying to use static mut with an AtomicUsize or AtomicBool:

static is what you want. static allows interior mutability, and that's what AtomicUsize and friends provide.

Upvotes: 2

Shepmaster
Shepmaster

Reputation: 431679

A static variable is guaranteed to have a single instance and you can take a reference to it. A const variable does not have this guarantee and the compiler is allowed to have zero, one, or multiple instances of it.

In your case, the code is equivalent to:

println!("{}", AtomicUsize::new(0).load(Ordering::SeqCst));
println!("{}", AtomicUsize::new(0).fetch_add(10, Ordering::SeqCst));
println!("{}", AtomicUsize::new(0).load(Ordering::SeqCst));

Since each value is created and thrown away, no changes from one propagate to the other.

In some ways, you can think of a const variable like a C or C++ #define — conceptually the value is just pasted wherever it's used.

Clippy 0.0.211 has a lint for this case:

error: a const item should never be interior mutable
 --> src/main.rs:3:1
  |
3 | const SOME_VAR: AtomicUsize = AtomicUsize::new(0);
  | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  | |
  | help: make this a static item: `static`
  |
  = note: #[deny(declare_interior_mutable_const)] on by default
  = help: for further information visit https://rust-lang-nursery.github.io/rust-clippy/v0.0.211/index.html#declare_interior_mutable_const

error: a const item with interior mutability should not be borrowed
 --> src/main.rs:6:20
  |
6 |     println!("{}", SOME_VAR.load(Ordering::SeqCst));
  |                    ^^^^^^^^
  |
  = note: #[deny(borrow_interior_mutable_const)] on by default
  = help: assign this const to a local or static variable, and use the variable here
  = help: for further information visit https://rust-lang-nursery.github.io/rust-clippy/v0.0.211/index.html#borrow_interior_mutable_const

In Java, I can use a final HashMap

Yes, you can make a non-thread-safe HashMap very easily in Java. Rust doesn't want to make it easy to create code that can lead to memory unsafety. You need to protect the type with appropriate safety, such as by a Mutex, or you need to dip into unsafe code if you the programmer guarantee that a global value will only ever be used by one thread.

See also:

Upvotes: 20

Related Questions