use std::sync::atomic::{AtomicU64, Ordering}; /// A thread-safe counter backed by an atomic integer. #[derive(Debug, Default)] pub struct Counter { value: AtomicU64, } impl Counter { /// Creates a counter with the given initial value. pub const fn new(initial: u64) -> Self { Self { value: AtomicU64::new(initial), } } /// Returns the current value. pub fn get(&self) -> u64 { self.value.load(Ordering::SeqCst) } /// Increments the counter by one and returns the updated value. pub fn increment(&self) -> u64 { self.add(1) } /// Adds `amount` and returns the updated value. pub fn add(&self, amount: u64) -> u64 { self.value.fetch_add(amount, Ordering::SeqCst) + amount } /// Resets the counter to zero and returns the previous value. pub fn reset(&self) -> u64 { self.value.swap(0, Ordering::SeqCst) } } impl From for Counter { fn from(value: u64) -> Self { Self::new(value) } } #[cfg(test)] mod tests { use super::Counter; use std::sync::Arc; use std::thread; #[test] fn basic_operations_work() { let counter = Counter::new(5); assert_eq!(counter.get(), 5); assert_eq!(counter.increment(), 6); assert_eq!(counter.add(4), 10); assert_eq!(counter.reset(), 10); assert_eq!(counter.get(), 0); } #[test] fn counts_safely_across_threads() { let counter = Arc::new(Counter::default()); let handles: Vec<_> = (0..8) .map(|_| { let counter = Arc::clone(&counter); thread::spawn(move || { for _ in 0..10_000 { counter.increment(); } }) }) .collect(); for handle in handles { handle.join().expect("worker thread panicked"); } assert_eq!(counter.get(), 80_000); } }