While Rc<T> provides shared ownership for single-threaded code, it cannot be safely shared across threads because its reference count operations are not atomic. For multi-threaded scenarios, Rust provides Arc<T> (Atomically Reference Counted), which uses atomic operations to safely manage reference counts across thread boundaries.
Arc<T> works similarly to Rc<T> but is thread-safe. The "atomic" part means that incrementing and decrementing the reference count is done using atomic CPU operations, which are guaranteed to complete without interference from other threads. This makes Arc<T> slightly slower than Rc<T> due to the synchronization overhead, so you should only use Arc<T> when you actually need to share data across threads.
Common use cases for Arc<T> include:
Mutex<T> or RwLock<T> for shared mutable state| Feature | Rc<T> | Arc<T> |
|---|---|---|
| Thread-safe | No | Yes |
| Performance | Faster | Slightly slower |
| Marker traits | !Send, !Sync | Send + Sync (if T is) |
| Use case | Single-threaded | Multi-threaded |
Arc::new(value) - Create a new atomic reference-counted pointerArc::clone(&arc) - Create a new reference (increments count atomically)Arc::strong_count(&arc) - Get the current number of strong referencesArc::weak_count(&arc) - Get the current number of weak referencesArc::downgrade(&arc) - Create a Weak<T> referenceweak.upgrade() - Try to convert a Weak<T> back to Arc<T>Implement the following types and functions to demonstrate Arc<T> patterns:
create_arc<T>(value: T) -> Arc<T> - Create a new atomic reference-counted valueclone_arc<T>(arc: &Arc<T>) -> Arc<T> - Clone a reference (increment count)get_strong_count<T>(arc: &Arc<T>) -> usize - Get the strong reference countget_value<T: Clone>(arc: &Arc<T>) -> T - Get a clone of the inner valueSharedConfig - A thread-safe configuration that can be shared across threads:
new( app_name: String, max_connections: usize, debug_mode: bool ) -> Arc<Self>app_name(&self) -> &strmax_connections(&self) -> usizedebug_mode(&self) -> boolcreate_weak<T>(arc: &Arc<T>) -> Weak<T> - Create a weak referenceupgrade_weak<T>(weak: &Weak<T>) -> Option<Arc<T>> - Try to upgrade a weak referenceget_weak_count<T>(arc: &Arc<T>) -> usize - Get the weak reference countAtomicCounter - A thread-safe counter using Arc<AtomicUsize>:
new() -> Self - Create with initial value 0new_with_value(value: usize) -> Self - Create with specific valueget(&self) -> usize - Get current valueincrement(&self) -> usize - Increment and return previous valuedecrement(&self) -> usize - Decrement and return previous valueadd(&self, val: usize) -> usize - Add and return previous valueclone_counter(&self) -> Self - Create another handle to the same counterSharedVec<T> - A thread-safe vector using Arc<Mutex<Vec<T>>>:
new() -> Self - Create empty shared vecpush(&self, value: T) - Add an elementpop(&self) -> Option<T> - Remove and return last elementlen(&self) -> usize - Get current lengthis_empty(&self) -> bool - Check if emptyget(&self, index: usize) -> Option<T> where T: Clone - Get element at indexclone_vec(&self) -> Self - Create another handle to the same vecuse arc_thread_safety::*;
use std::sync::Arc;
use std::thread;
// Basic Arc operations
let shared = create_arc(42);
assert_eq!(get_strong_count(&shared), 1);
let cloned = clone_arc(&shared);
assert_eq!(get_strong_count(&shared), 2);
// Sharing across threads
let data = create_arc(vec![1, 2, 3]);
let data_clone = clone_arc(&data);
let handle = thread::spawn(move || {
// data_clone is accessible in this thread
assert_eq!(data_clone.len(), 3);
});
handle.join().unwrap();
// Shared configuration
let config = SharedConfig::new(
"MyApp".to_string(),
100,
true
);
let config_clone = Arc::clone(&config);
thread::spawn(move || {
assert_eq!(config_clone.app_name(), "MyApp");
}).join().unwrap();
// Atomic counter shared across threads
let counter = AtomicCounter::new();
let counter_clone = counter.clone_counter();
let handle = thread::spawn(move || {
for _ in 0..1000 {
counter_clone.increment();
}
});
for _ in 0..1000 {
counter.increment();
}
handle.join().unwrap();
assert_eq!(counter.get(), 2000);
// Thread-safe vector
let vec = SharedVec::new();
vec.push(1);
vec.push(2);
assert_eq!(vec.len(), 2);Arc::new() to wrap values for thread-safe shared ownershipArc::clone(&arc) is cheap - it only atomically increments a counterArc with Mutex: Arc<Mutex<T>>AtomicUsize provides lock-free atomic operations like fetch_add and fetch_subOrdering::SeqCst for atomic operations if unsure about memory orderingMutex, use .lock().unwrap() to acquire the lock (panics if poisoned)Weak<T> works the same way with Arc as it does with RcArc<T> is Send + Sync only if T is Send + Sync