Rust Guide > Documentation > Concurrency > AtomicUsize

Introduction

In Rust, AtomicUsize is an atomic integer type that provides thread-safe operations on the platform's native pointer-sized unsigned integers. It is part of the standard library's std::sync::atomic module and is useful for shared state between threads where you need to perform atomic operations without using locks.

Using AtomicUsize

To use AtomicUsize, you need to import it from the std::sync::atomic module. You can create an AtomicUsize by using the AtomicUsize::new method. Atomic operations such as store, load, fetch_add, and compare_and_swap can be used to manipulate the value safely across threads.

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

fn main() {
    let atomic = AtomicUsize::new(5);

    println!("Initial value: {}", atomic.load(Ordering::Relaxed));

    atomic.store(10, Ordering::Relaxed);

    println!("Updated value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Initial value: 5
// Updated value: 10

Key Methods

Below are some of the key methods exposed by the AtomicUSize class:

AtomicUsize::new

Creates a new AtomicUsize with the given initial value.

use std::sync::atomic::AtomicUsize;

fn main() {
    let atomic = AtomicUsize::new(10);
    println!("Created AtomicUsize with initial value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Created AtomicUsize with initial value: 10

load

Returns the current value of the AtomicUsize.

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

fn main() {
    let atomic = AtomicUsize::new(20);
    println!("Current value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Current value: 20

store

Sets the value of the AtomicUsize.

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

fn main() {
    let atomic = AtomicUsize::new(30);
    atomic.store(40, Ordering::Relaxed);
    println!("Updated value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Updated value: 40

compare_and_swap

Compares the current value with current, and if they are equal, sets it to new.

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

fn main() {
    let atomic = AtomicUsize::new(50);
    let old_value = atomic.compare_and_swap(50, 60, Ordering::Relaxed);
    println!("Old value: {}, New value: {}", old_value, atomic.load(Ordering::Relaxed));
}
// Output:
// Old value: 50
// New value: 60

fetch_add

Adds to the current value, returning the previous value.

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

fn main() {
    let atomic = AtomicUsize::new(70);
    let previous = atomic.fetch_add(10, Ordering::Relaxed);
    println!("Previous value: {}, New value: {}", previous, atomic.load(Ordering::Relaxed));
}
// Output:
// Previous value: 70
// New value: 80

fetch_sub

Subtracts from the current value, returning the previous value.

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

fn main() {
    let atomic = AtomicUsize::new(100);
    let previous = atomic.fetch_sub(20, Ordering::Relaxed);
    println!("Previous value: {}, New value: {}", previous, atomic.load(Ordering::Relaxed));
}
// Output:
// Previous value: 100
// New value: 80

Example Usage

Example 1: Basic Usage

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

fn main() {
    let atomic = AtomicUsize::new(5);

    println!("Initial value: {}", atomic.load(Ordering::Relaxed));

    atomic.store(10, Ordering::Relaxed);

    println!("Updated value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Initial value: 5
// Updated value: 10

Example 2: Using compare_and_swap

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

fn main() {
    let atomic = AtomicUsize::new(50);

    let old_value = atomic.compare_and_swap(50, 60, Ordering::Relaxed);
    println!("Old value: {}, New value: {}", old_value, atomic.load(Ordering::Relaxed));
}
// Output:
// Old value: 50
// New value: 60

Example 3: Using fetch_add

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

fn main() {
    let atomic = AtomicUsize::new(70);

    let previous = atomic.fetch_add(10, Ordering::Relaxed);
    println!("Previous value: {}, New value: {}", previous, atomic.load(Ordering::Relaxed));
}
// Output:
// Previous value: 70
// New value: 80

Example 4: Using fetch_sub

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

fn main() {
    let atomic = AtomicUsize::new(100);

    let previous = atomic.fetch_sub(20, Ordering::Relaxed);
    println!("Previous value: {}, New value: {}", previous, atomic.load(Ordering::Relaxed));
}
// Output:
// Previous value: 100
// New value: 80

Example 5: Using with Multiple Threads

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

fn main() {
    let atomic = Arc::new(AtomicUsize::new(0));

    let atomic1 = Arc::clone(&atomic);
    let handle1 = thread::spawn(move || {
        atomic1.fetch_add(1, Ordering::Relaxed);
    });

    let atomic2 = Arc::clone(&atomic);
    let handle2 = thread::spawn(move || {
        atomic2.fetch_add(1, Ordering::Relaxed);
    });

    handle1.join().unwrap();
    handle2.join().unwrap();

    println!("Final value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Final value: 2

Considerations

  • Operations on AtomicUsize are lock-free and thread-safe.
  • Always use the appropriate Ordering when performing atomic operations to ensure the correct memory ordering guarantees.
  • Use AtomicUsize when you need to share a simple integer state across threads without the overhead of using a mutex.

See Also

  • AtomicU32 - An atomic integer type for shared state between threads, specifically for 32-bit unsigned integers.
  • Mutex - A mutual exclusion primitive for protecting shared data.
  • RwLock - A read-write lock allowing multiple readers or a single writer.