Skip to main content

once_arc/
lib.rs

1//! # once-arc
2//!
3//! Initialize-once [`Arc<T>`](std::sync::Arc) containers with zero-cost reads.
4//!
5//! This crate provides two types for sharing data across threads where the
6//! value is written once and read many times:
7//!
8//! - **[`OnceArc`]** — a lock-free, CAS-based initialize-once slot.
9//!   You construct the [`Arc<T>`](std::sync::Arc) yourself and store it with an explicit
10//!   [`Ordering`](std::sync::atomic::Ordering).
11//!
12//! - **[`InitOnceArc`]** — synchronized one-time initialization via a closure
13//!   (similar to [`OnceLock`](std::sync::OnceLock)). The closure runs exactly
14//!   once; its return value becomes the stored `Arc<T>`.
15//!
16//! Both types share the same fast path: once the value is set,
17//! [`get()`](OnceArc::get) is a **single atomic load** — no
18//! locking, no CAS, no reference-count manipulation. On x86 this compiles
19//! to a plain `mov`.
20//!
21//! # Why not `OnceLock<Arc<T>>`?
22//!
23//! [`OnceLock`](std::sync::OnceLock) stores its value inline, so
24//! `get()` returns `&Arc<T>` — two pointer indirections to reach `T`.
25//! Here, the atomic *is* the `Arc`'s pointer, so `get()` returns `&T`
26//! directly.
27//!
28//! # Examples
29//!
30//! ```
31//! use std::sync::Arc;
32//! use std::sync::atomic::Ordering;
33//! use once_arc::OnceArc;
34//!
35//! let slot = OnceArc::new();
36//! slot.store(Arc::new(42), Ordering::Release).unwrap();
37//! assert_eq!(slot.get(Ordering::Acquire), Some(&42));
38//! ```
39//!
40//! ```
41//! use std::sync::Arc;
42//! use std::sync::atomic::Ordering;
43//! use once_arc::InitOnceArc;
44//!
45//! let cell = InitOnceArc::new();
46//! cell.init(|| Arc::new("hello")).unwrap();
47//! assert_eq!(cell.get(Ordering::Acquire), Some(&"hello"));
48//! ```
49
50mod init_once_arc;
51mod once_arc;
52
53pub use init_once_arc::InitOnceArc;
54pub use once_arc::OnceArc;