diff options
| author | Jubilee Young <workingjubilee@gmail.com> | 2024-05-27 13:21:38 -0700 |
|---|---|---|
| committer | Jubilee Young <workingjubilee@gmail.com> | 2024-06-02 22:53:41 -0700 |
| commit | 9ed7cfc952b06fb4e552fb02bbd65070e56cfdc0 (patch) | |
| tree | fe73cddb73ea5fd151c2183350541aef6e6a7da4 | |
| parent | 2d0ebca9798f76ff5ff1ef171cdf73fb5c8d7127 (diff) | |
| download | rust-9ed7cfc952b06fb4e552fb02bbd65070e56cfdc0.tar.gz rust-9ed7cfc952b06fb4e552fb02bbd65070e56cfdc0.zip | |
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.
| -rw-r--r-- | library/std/src/sync/once_lock.rs | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index d52f610c1f4..22083bc2933 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -8,8 +8,14 @@ use crate::sync::Once; /// A synchronization primitive which can be written to only once. /// /// This type is a thread-safe [`OnceCell`], and can be used in statics. +/// In many simple cases, you can use [`LazyLock<T, F>`] instead to get the benefits of this type +/// with less effort: `LazyLock<T, F>` "looks like" `&T` because it initializes with `F` on deref! +/// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock +/// doesn't allow additional inputs to its function after you call [`LazyLock::new(|| ...)`]. /// /// [`OnceCell`]: crate::cell::OnceCell +/// [`LazyLock<T, F>`]: crate::sync::LazyLock +/// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new /// /// # Examples /// @@ -37,6 +43,55 @@ use crate::sync::Once; /// Some(&12345), /// ); /// ``` +/// +/// You can use `OnceLock` to implement a type that requires "append-only" logic: +/// +/// ``` +/// use std::sync::{OnceLock, atomic::{AtomicU32, Ordering}}; +/// use std::thread; +/// +/// struct OnceList<T> { +/// data: OnceLock<T>, +/// next: OnceLock<Box<OnceList<T>>>, +/// } +/// impl<T> OnceList<T> { +/// const fn new() -> OnceList<T> { +/// OnceList { data: OnceLock::new(), next: OnceLock::new() } +/// } +/// fn push(&self, value: T) { +/// // FIXME: this impl is concise, but is also slow for long lists or many threads. +/// // as an exercise, consider how you might improve on it while preserving the behavior +/// if let Err(value) = self.data.set(value) { +/// let next = self.next.get_or_init(|| Box::new(OnceList::new())); +/// next.push(value) +/// }; +/// } +/// fn contains(&self, example: &T) -> bool +/// where +/// T: PartialEq, +/// { +/// self.data.get().map(|item| item == example).filter(|v| *v).unwrap_or_else(|| { +/// self.next.get().map(|next| next.contains(example)).unwrap_or(false) +/// }) +/// } +/// } +/// +/// // Let's exercise this new Sync append-only list by doing a little counting +/// static LIST: OnceList<u32> = OnceList::new(); +/// static COUNTER: AtomicU32 = AtomicU32::new(0); +/// +/// let vec = (0..thread::available_parallelism().unwrap().get()).map(|_| thread::spawn(|| { +/// while let i @ 0..=1000 = COUNTER.fetch_add(1, Ordering::Relaxed) { +/// LIST.push(i); +/// } +/// })).collect::<Vec<thread::JoinHandle<_>>>(); +/// vec.into_iter().for_each(|handle| handle.join().unwrap()); +/// +/// for i in 0..=1000 { +/// assert!(LIST.contains(&i)); +/// } +/// +/// ``` #[stable(feature = "once_cell", since = "1.70.0")] pub struct OnceLock<T> { once: Once, |
