Rust Guide > Documentation > Concurrency > AtomicI32

Introduction

The AtomicI32 class in Rust provides a way to perform atomic operations on a 32-bit integer. It is part of Rust's standard library and is typically used for safe concurrent programming, allowing multiple threads to manipulate shared data without introducing data races.

Using AtomicI32

The AtomicI32 class can be used to create a thread-safe integer that supports atomic operations. Here is a basic example:

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

fn main() {
    let atomic_val = AtomicI32::new(5);
    atomic_val.store(10, Ordering::SeqCst);
    let value = atomic_val.load(Ordering::SeqCst);
    println!("Value: {}", value);
}
// Output: Value: 10

Key Methods

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

new

Creates a new AtomicI32 initialized to the given value.

use std::sync::atomic::AtomicI32;

fn main() {
    let atomic_val = AtomicI32::new(0);
    println!("Initial value: {}", atomic_val.load(Ordering::SeqCst));
}
// Output: Initial value: 0

load

Loads a value from the AtomicI32. Takes an Ordering argument to specify the memory ordering of the operation.

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

fn main() {
    let atomic_val = AtomicI32::new(1);
    let value = atomic_val.load(Ordering::Relaxed);
    println!("Loaded value: {}", value);
}
// Output: Loaded value: 1

store

Stores a value into the AtomicI32. Takes an Ordering argument to specify the memory ordering of the operation.

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

fn main() {
    let atomic_val = AtomicI32::new(1);
    atomic_val.store(2, Ordering::Relaxed);
    println!("Stored value: {}", atomic_val.load(Ordering::Relaxed));
}
// Output: Stored value: 2

swap

Swaps the current value with a new value. Returns the previous value. Takes an Ordering argument to specify the memory ordering of the operation.

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

fn main() {
    let atomic_val = AtomicI32::new(1);
    let old_value = atomic_val.swap(3, Ordering::SeqCst);
    println!("Old value: {}, New value: {}", old_value, atomic_val.load(Ordering::SeqCst));
}
// Output: Old value: 1, New value: 3

compare_and_swap

Compares the current value with a specified value and, if they are equal, sets the current value to a new value. Returns the previous value. This method is deprecated in favor of compare_exchange.

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

fn main() {
    let atomic_val = AtomicI32::new(1);
    let old_value = atomic_val.compare_and_swap(1, 4, Ordering::SeqCst);
    println!("Old value: {}, New value: {}", old_value, atomic_val.load(Ordering::SeqCst));
}
// Output: Old value: 1, New value: 4

fetch_add

Adds to the current value, returning the previous value. Takes an Ordering argument to specify the memory ordering of the operation.

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

fn main() {
    let atomic_val = AtomicI32::new(1);
    let prev_value = atomic_val.fetch_add(5, Ordering::SeqCst);
    println!("Previous value: {}, Current value: {}", prev_value, atomic_val.load(Ordering::SeqCst));
}
// Output: Previous value: 1, Current value: 6

fetch_sub

Subtracts from the current value, returning the previous value. Takes an Ordering argument to specify the memory ordering of the operation.

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

fn main() {
    let atomic_val = AtomicI32::new(10);
    let prev_value = atomic_val.fetch_sub(3, Ordering::SeqCst);
    println!("Previous value: {}, Current value: {}", prev_value, atomic_val.load(Ordering::SeqCst));
}
// Output: Previous value: 10, Current value: 7

Example Usage

Example 1: Basic Initialization and Load

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

fn main() {
    let atomic_val = AtomicI32::new(42);
    let value = atomic_val.load(Ordering::SeqCst);
    println!("Loaded value: {}", value);
}
// Output: Loaded value: 42

Example 2: Atomic Store

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

fn main() {
    let atomic_val = AtomicI32::new(5);
    atomic_val.store(10, Ordering::SeqCst);
    println!("Stored value: {}", atomic_val.load(Ordering::SeqCst));
}
// Output: Stored value: 10

Example 3: Atomic Swap

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

fn main() {
    let atomic_val = AtomicI32::new(1);
    let old_value = atomic_val.swap(3, Ordering::SeqCst);
    println!("Old value: {}, New value: {}", old_value, atomic_val.load(Ordering::SeqCst));
}
// Output: Old value: 1, New value: 3

Example 4: Compare and Swap

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

fn main() {
    let atomic_val = AtomicI32::new(1);
    let old_value = atomic_val.compare_and_swap(1, 4, Ordering::SeqCst);
    println!("Old value: {}, New value: {}", old_value, atomic_val.load(Ordering::SeqCst));
}
// Output: Old value: 1, New value: 4

Example 5: Fetch Add

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

fn main() {
    let atomic_val = AtomicI32::new(1);
    let prev_value = atomic_val.fetch_add(5, Ordering::SeqCst);
    println!("Previous value: {}, Current value: {}", prev_value, atomic_val.load(Ordering::SeqCst));
}
// Output: Previous value: 1, Current value: 6

Considerations

  • AtomicI32 provides low-level atomic operations for concurrency. Proper use of memory ordering (e.g., Ordering::Relaxed, Ordering::SeqCst) is essential to ensure correctness.
  • Using atomic operations can be complex and error-prone; consider higher-level synchronization primitives like mutexes if simpler concurrency management is needed.

See Also

  • AtomicU32 - Similar to AtomicI32, but for unsigned 32-bit integers.
  • Mutex - A mutual exclusion primitive useful for protecting shared data.
  • RwLock - A reader-writer lock for allowing concurrent reads or exclusive writes.
  • Arc - An atomic reference-counted smart pointer for shared ownership across threads.