arc
import std::alloc::arc; · source
Arc<T, A>
type struct Arc<T, A = GlobalAlloc> {
inner: ArcInner<T, A>*;
static new(value: T) -> Arc<T, GlobalAlloc>;
static new_in(value: T, alloc: A) -> Arc<T, A>;
static try_new(value: T) -> Result<Arc<T, GlobalAlloc>, AllocError>;
static try_new_in(value: T, alloc: A) -> Result<Arc<T, A>, AllocError>;
clone(&this) -> Arc<T, A>;
downgrade(&this) -> Weak<T, A>;
strong_count(&this) -> u64;
weak_count(&this) -> u64;
as_ptr(&this) -> T*;
get_ref(&this) -> T*;
get_mut(mut &this) -> Option<T*>;
into_raw(mut this) -> ArcInner<T, A>*;
static from_raw(p: ArcInner<T, A>*) -> Arc<T, A>;
static increment_strong_count(p: ArcInner<T, A>*) -> void;
static decrement_strong_count(p: ArcInner<T, A>*) -> void
where T: Drop, A: Allocator;
}
Shared ownership across threads. Arc is the thread-safe sibling of Rc. Same shape, same two-phase teardown, but the counts are Atomic<u64>, so handles can be cloned and dropped concurrently without a data race. It is structurally Send + Sync whenever T and A are.
import std::alloc::arc;
import std::thread;
mut shared: Arc<Table> = Arc<Table>::new(table);
mut copy: Arc<Table> = shared.clone();
thread::spawn(worker, copy);
The API mirrors Rc — new, new_in, try_new, try_new_in, clone, downgrade, get_ref, get_mut, strong_count, weak_count — with upgrade on Weak running a compare-exchange loop so it can never resurrect a value whose strong count already reached zero.
get_mut requires strong == 1 && weak == 1. With a single strong reference no other thread holds an Arc to clone or downgrade from, so once both counts read 1 the access really is exclusive. Even then, interior mutation visible across threads needs the value itself to be Sync — an atomic, say.
Weak<T, A>
type struct Weak<T, A = GlobalAlloc> {
inner: ArcInner<T, A>*;
static new() -> Weak<T, GlobalAlloc>;
upgrade(&this) -> Option<Arc<T, A>>;
clone(&this) -> Weak<T, A>;
strong_count(&this) -> u64;
}
The same shape as rc::Weak: it does not keep the value alive, upgrade returns None once the last strong handle is gone, and Weak::new() is the placeholder that never upgrades. The difference is that upgrade here is a compare-exchange loop, so it can never resurrect a value whose strong count another thread has already taken to zero.
The raw-pointer escape hatch
For parking a reference outside the type system — a manual-vtable waker, a C callback's void* — four statics operate on the header pointer directly:
into_rawconsumes a handle without touching the count.from_rawtakes the reference back as a handle.increment_strong_countadds one to a header held only as a raw pointer, asclonewould.decrement_strong_countremoves one, running the destructor and freeing the header if it was the last — asdropwould.
Each into_raw or increment_strong_count must be balanced exactly once by from_raw or decrement_strong_count, or the value leaks. Unbalancing in the other direction is undefined.
The memory ordering, briefly
type struct ArcInner<T, A> {
strong: Atomic<u64>;
weak: Atomic<u64>;
value: T;
alloc: A;
}
Clone bumps the count with Relaxed; drop decrements with Release and the last-decrement path issues an Acquire fence. This is the canonical Boost/Rust pattern. Relaxed is right for the clone because the new reference is only ever observed by callers who already share a happens-before edge with the cloning thread. The release/acquire pair on drop is what synchronizes the final T::drop() against every prior mutation from any thread that has already dropped its own handle.
Routing every free decision through the one atomic weak counter is what makes the race between "the last Arc drops" and "a Weak drops" safe: there is never a moment where two threads read two separate counters and both decide to free.
Trait implementations
implement<T, A> trait Drop for struct Arc<T, A>
where T: Drop, A: Allocator
implement<T, A> trait Drop for struct Weak<T, A>
where T: Drop, A: Allocator