Add "OnceList" example to motivate OnceLock · model-checking/verify-rust-std@06d9b0e
@@ -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,55 @@ 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+/// use std::sync::{OnceLock, atomic::{AtomicU32, Ordering}};
51+/// use std::thread;
52+///
53+/// struct OnceList<T> {
54+/// data: OnceLock<T>,
55+/// next: OnceLock<Box<OnceList<T>>>,
56+/// }
57+/// impl<T> OnceList<T> {
58+/// const fn new() -> OnceList<T> {
59+/// OnceList { data: OnceLock::new(), next: OnceLock::new() }
60+/// }
61+/// fn push(&self, value: T) {
62+/// // FIXME: this impl is concise, but is also slow for long lists or many threads.
63+/// // as an exercise, consider how you might improve on it while preserving the behavior
64+/// if let Err(value) = self.data.set(value) {
65+/// let next = self.next.get_or_init(|| Box::new(OnceList::new()));
66+/// next.push(value)
67+/// };
68+/// }
69+/// fn contains(&self, example: &T) -> bool
70+/// where
71+/// T: PartialEq,
72+/// {
73+/// self.data.get().map(|item| item == example).filter(|v| *v).unwrap_or_else(|| {
74+/// self.next.get().map(|next| next.contains(example)).unwrap_or(false)
75+/// })
76+/// }
77+/// }
78+///
79+/// // Let's exercise this new Sync append-only list by doing a little counting
80+/// static LIST: OnceList<u32> = OnceList::new();
81+/// static COUNTER: AtomicU32 = AtomicU32::new(0);
82+///
83+/// let vec = (0..thread::available_parallelism().unwrap().get()).map(|_| thread::spawn(|| {
84+/// while let i @ 0..=1000 = COUNTER.fetch_add(1, Ordering::Relaxed) {
85+/// LIST.push(i);
86+/// }
87+/// })).collect::<Vec<thread::JoinHandle<_>>>();
88+/// vec.into_iter().for_each(|handle| handle.join().unwrap());
89+///
90+/// for i in 0..=1000 {
91+/// assert!(LIST.contains(&i));
92+/// }
93+///
94+/// ```
4095#[stable(feature = "once_cell", since = "1.70.0")]
4196pub struct OnceLock<T> {
4297once: Once,