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")]

4196

pub struct OnceLock<T> {

4297

once: Once,