once_list/
lib.rs

1use std::sync::OnceLock;
2
3/// ```
4/// use std::sync::{atomic::{AtomicU32, Ordering}};
5/// use std::thread;
6/// use once_list::OnceList;
7///
8/// // Let's exercise this new Sync append-only list by doing a little counting
9/// static LIST: OnceList<u32> = OnceList::new();
10/// static COUNTER: AtomicU32 = AtomicU32::new(0);
11///
12/// let vec = (0..thread::available_parallelism().unwrap().get()).map(|_| thread::spawn(|| {
13///     while let i @ 0..1000 = COUNTER.fetch_add(1, Ordering::Relaxed) {
14///         LIST.push(i);
15///     }
16/// })).collect::<Vec<thread::JoinHandle<_>>>();
17/// vec.into_iter().for_each(|handle| handle.join().unwrap());
18///
19/// for i in 0..1000 {
20///     assert!(LIST.contains(&i));
21/// }
22/// ```
23#[derive(Clone, Debug, PartialEq, Eq)]
24pub struct OnceList<T> {
25    data: OnceLock<T>,
26    next: OnceLock<Box<OnceList<T>>>,
27}
28impl<T> Default for OnceList<T> {
29    fn default() -> Self {
30        OnceList::new()
31    }
32}
33impl<T> OnceList<T> {
34    pub const fn new() -> OnceList<T> {
35        OnceList { data: OnceLock::new(), next: OnceLock::new() }
36    }
37    pub fn push(&self, value: T) {
38        // FIXME: this impl is concise, but is also slow for long lists or many threads.
39        // as an exercise, consider how you might improve on it while preserving the behavior
40        if let Err(value) = self.data.set(value) {
41            let next = self.next.get_or_init(|| Box::new(OnceList::new()));
42            next.push(value)
43        };
44    }
45    pub fn contains(&self, example: &T) -> bool
46    where
47        T: PartialEq,
48    {
49        self.data
50            .get()
51            .and_then(|item| (item == example).then_some(true))
52            .unwrap_or_else(|| self.next.get().map(|next| next.contains(example)).unwrap_or(false))
53    }
54}