Ronald Smith
Ronald Smith

Reputation: 359

Why do Rust's `Atomic*` types use non-mutable functions to mutate the value?

I notice that Rust's Atomic* structs have functions which modify the value, such as fetch_add. For instance, I can write this program:

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

struct Tester {
    counter: AtomicUsize
}

impl Tester {
    fn run(&self) {
        let counter = self.counter.fetch_add(1, Ordering::Relaxed);
        println!("Hi there, this has been run {} times", counter);
    }
}

fn main() {
    let t = Tester { counter: AtomicUsize::new(0) };
    t.run();
    t.run();
}

This compiles and runs fine, but if I change the AtomicUsize to a normal integer, it will (correctly) fail to compile due to mutability concerns:

struct Tester {
    counter: u64
}

impl Tester {
    fn run(&self) {
        self.counter = self.counter + 1;
        println!("Hi there, this has been run {} times", self.counter);
    }
}

Upvotes: 15

Views: 1804

Answers (1)

Chris Morgan
Chris Morgan

Reputation: 90852

It wouldn’t be very useful if it didn’t work this way. With &mut references, only one can exist at a time, and no & references at that time, so the whole question of atomicity of operation would be moot.

Another way of looking at it is &mut are unique references, and & aliasable references. For normal types, mutation can only safely occur if you have a unique reference, but atomic types are all about mutation (via replacement) without needing a unique reference.

The naming of & and &mut has been a fraught matter, with much fear, uncertainty and doubt in the community and documents like Focusing on Ownership explaining how things actually are. The language has ended up staying with & and &mut, but &mut is actually about uniqueness rather than mutability (it’s just that most of the time the two are equivalent).

Upvotes: 23

Related Questions