Skip to content

Commit a621faf

Browse files
Add "OnceList" example to motivate OnceLock
While slightly verbose, it helps explain "why bother with OnceLock?" This is a point of confusion that has been raised multiple times shortly before and after the stabilization of LazyLock.
1 parent 60d8141 commit a621faf

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

library/std/src/sync/once_lock.rs

+56
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,14 @@ use crate::sync::Once;
88
/// A synchronization primitive which can be written to only once.
99
///
1010
/// This type is a thread-safe [`OnceCell`], and can be used in statics.
11+
/// In many simple cases, you can use [`LazyLock<T, F>`] instead to get the benefits of this type
12+
/// with less effort: `LazyLock<T, F>` "looks like" `&T` because it initializes with `F` on deref!
13+
/// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock
14+
/// doesn't allow additional inputs to its function after you call [`LazyLock::new(|| ...)`].
1115
///
1216
/// [`OnceCell`]: crate::cell::OnceCell
17+
/// [`LazyLock<T, F>`]: crate::sync::LazyLock
18+
/// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new
1319
///
1420
/// # Examples
1521
///
@@ -37,6 +43,56 @@ use crate::sync::Once;
3743
/// Some(&12345),
3844
/// );
3945
/// ```
46+
///
47+
/// You can use `OnceLock` to implement a type that requires "append-only" logic:
48+
///
49+
/// ```
50+
/// #![feature(once_cell_try_insert)]
51+
/// use std::sync::{OnceLock, atomic::{AtomicU32, Ordering}};
52+
/// use std::thread;
53+
///
54+
/// struct OnceList<T> {
55+
/// data: OnceLock<T>,
56+
/// next: OnceLock<Box<OnceList<T>>>,
57+
/// }
58+
/// impl<T> OnceList<T> {
59+
/// const fn new() -> OnceList<T> {
60+
/// OnceList { data: OnceLock::new(), next: OnceLock::new() }
61+
/// }
62+
/// fn push(&self, value: T) {
63+
/// // FIXME: this impl is concise, but is also slow for long lists or many threads.
64+
/// // as an exercise, consider how you might improve on it while preserving the behavior
65+
/// if let Err((_, value)) = self.data.try_insert(value) {
66+
/// let next = self.next.get_or_init(|| Box::new(OnceList::new()));
67+
/// next.push(value)
68+
/// };
69+
/// }
70+
/// fn contains(&self, example: &T) -> bool
71+
/// where
72+
/// T: PartialEq,
73+
/// {
74+
/// self.data.get().map(|item| item == example).filter(|v| *v).unwrap_or_else(|| {
75+
/// self.next.get().map(|next| next.contains(example)).unwrap_or(false)
76+
/// })
77+
/// }
78+
/// }
79+
///
80+
/// // Let's exercise this new Sync append-only list by doing a little counting
81+
/// static LIST: OnceList<u32> = OnceList::new();
82+
/// static COUNTER: AtomicU32 = AtomicU32::new(0);
83+
///
84+
/// let vec = (0..thread::available_parallelism().unwrap().get()).map(|_| thread::spawn(|| {
85+
/// while let i @ 0..1000 = COUNTER.fetch_add(1, Ordering::Relaxed) {
86+
/// LIST.push(i);
87+
/// }
88+
/// })).collect::<Vec<thread::JoinHandle<_>>>();
89+
/// vec.into_iter().for_each(|handle| handle.join().unwrap());
90+
///
91+
/// for i in 0..1000 {
92+
/// assert!(LIST.contains(&i));
93+
/// }
94+
///
95+
/// ```
4096
#[stable(feature = "once_cell", since = "1.70.0")]
4197
pub struct OnceLock<T> {
4298
once: Once,

0 commit comments

Comments
 (0)