deferred_cell/
lib.rs

1//! `deferred-cell`: A single-assignment, weak reference wrapper for write-once cyclic node graphs with late initialization.
2//!
3//! This crate provides a lightweight alternative to runtime mutation or interior mutability
4//! when building write-once reference graphs such as cyclic trees or bidirectional structures.
5//!
6//! `Deferred<T>` enables building self-referential or cyclic structures without interior mutability.
7//! It starts uninitialized, and can be set *once* with a weak reference to a value of type `T`.
8//!
9//! To assign a value, use [`SetOnce::from`] followed by [`SetOnce::try_set`].
10//!
11//! After initialization, the reference can be accessed using [`Deferred::get`] or [`Deferred::try_get`].
12//!
13//! ## Example
14//!
15//! ```rust
16//! use deferred_cell::{Deferred, SetOnce, DeferredError};
17//! use std::rc::Rc;
18//!
19//! struct Node {
20//!     value: String,
21//!     neighbor: Deferred<Node>,
22//! }
23//!
24//! fn main() -> Result<(), DeferredError> {
25//!     let node = Rc::new(Node {
26//!         value: "A".into(),
27//!         neighbor: Deferred::default(),
28//!     });
29//!     let neighbor = Rc::new(Node {
30//!         value: "B".into(),
31//!         neighbor: Deferred::default(),
32//!     });
33//!
34//!     SetOnce::from(&node.neighbor).try_set(&neighbor)?;
35//!     let linked = node.neighbor.try_get()?;
36//!     assert_eq!(linked.value, "B");
37//!     // Calling `get()` will panic if node.neighbor is not initialized!
38//!     assert_eq!(node.neighbor.get().value, "B");
39//!
40//!     Ok(())
41//! }
42//! ```
43#![deny(clippy::unwrap_used, clippy::expect_used)]
44#![warn(clippy::all, clippy::nursery)]
45
46use std::{
47    cell::OnceCell,
48    rc::{Rc, Weak},
49};
50
51use thiserror::Error;
52
53/// Errors thrown by deferred-cell
54#[derive(Error, Debug)]
55#[non_exhaustive]
56pub enum DeferredError {
57    #[error("Cannot initialize Deferred twice!")]
58    DuplicateInitialization(),
59    #[error("Cannot use uninitialized value!")]
60    NotInitializedError(),
61}
62
63/// A write-once, weak reference wrapper for late initialization.
64///
65/// Use [`SetOnce`](crate::SetOnce) to assign a value exactly once,
66#[derive(Debug, Clone)]
67pub struct Deferred<T>(OnceCell<Weak<T>>);
68
69impl<T> Default for Deferred<T> {
70    fn default() -> Self {
71        Self(OnceCell::new())
72    }
73}
74
75impl<T> Deferred<T> {
76    pub fn try_get(&self) -> Result<Rc<T>, DeferredError> {
77        self.0
78            .get()
79            .ok_or(DeferredError::NotInitializedError())?
80            .upgrade()
81            .ok_or(DeferredError::NotInitializedError())
82    }
83    #[must_use]
84    pub fn get(&self) -> Rc<T> {
85        #[allow(clippy::expect_used)]
86        self.try_get().expect("Deferred value is not yet set!")
87    }
88    #[inline]
89    pub fn is_ready(&self) -> bool {
90        self.0.get().is_some()
91    }
92}
93
94/// A write-once assignment interface for [`Deferred<T>`].
95///
96/// `SetOnce<'a, T>` is a lightweight wrapper used to initialize a [`Deferred<T>`]
97/// exactly one time, enforcing single-assignment semantics.
98///
99/// You typically create it via [`SetOnce::from`] and assign a value using [`SetOnce::try_set`].
100///
101/// # Example
102/// ```
103/// use deferred_cell::{Deferred, SetOnce};
104/// use std::rc::Rc;
105///
106/// let deferred = Deferred::default();
107/// let value = Rc::new(42);
108/// SetOnce::from(&deferred).try_set(&value).unwrap();
109/// ```
110#[derive(Debug, Clone)]
111pub struct SetOnce<'a, T>(&'a Deferred<T>);
112
113impl<'a, T> SetOnce<'a, T> {
114    pub const fn from(cell: &'a Deferred<T>) -> Self {
115        Self(cell)
116    }
117    pub fn try_set(&self, value: &Rc<T>) -> Result<(), DeferredError> {
118        self.0
119            .0
120            .set(Rc::downgrade(value))
121            .map_err(|_| DeferredError::DuplicateInitialization())
122    }
123    #[inline]
124    pub fn can_set(&self) -> bool {
125        self.0.0.get().is_none()
126    }
127}
128
129/// Iterator extension trait to improve the ergonomics of `Deferred<T>` collections
130pub trait DeferredIteratorExt<T>: Iterator<Item = Deferred<T>> + Sized {
131    /// Returns an iterator of `Rc<T>` from an iterator of `Deferred<T>`.
132    ///
133    /// # Panics
134    /// Panics if any `Deferred<T>` is not initialized.
135    fn get_deferred(self) -> impl Iterator<Item = Rc<T>> {
136        self.map(|d| d.get())
137    }
138    /// Returns an iterator of `Result<Rc<T>, DeferredError>` from an iterator of `Deferred<T>`.
139    fn try_get_deferred(self) -> impl Iterator<Item = Result<Rc<T>, DeferredError>> {
140        self.map(|d| d.try_get())
141    }
142}
143
144impl<T, I> DeferredIteratorExt<T> for I where I: Iterator<Item = Deferred<T>> {}
145
146// Allowed in tests
147#[allow(clippy::unwrap_used)]
148#[cfg(test)]
149mod test {
150    use super::*;
151
152    #[derive(Debug, Clone)]
153    struct Node {
154        value: String,
155        neighbors: Vec<Deferred<Node>>,
156    }
157    impl Node {
158        fn new(value: &str, n_neighbors: usize) -> Rc<Self> {
159            Rc::new(Self {
160                value: value.into(),
161                neighbors: (0..n_neighbors)
162                    .map(|_| Deferred::default())
163                    .collect::<Vec<_>>(),
164            })
165        }
166    }
167
168    fn make_cyclic_graph() -> Vec<Rc<Node>> {
169        /*
170                   North
171                /    |     \
172            East - Center - West
173                \    |     /
174                   South
175        */
176        let center = Node::new("Center", 4);
177        let north = Node::new("North", 3);
178        let east = Node::new("East", 3);
179        let south = Node::new("South", 3);
180        let west = Node::new("West", 3);
181
182        SetOnce::from(&center.neighbors[0]).try_set(&north).unwrap();
183        SetOnce::from(&center.neighbors[1]).try_set(&west).unwrap();
184        SetOnce::from(&center.neighbors[2]).try_set(&south).unwrap();
185        SetOnce::from(&center.neighbors[3]).try_set(&east).unwrap();
186
187        SetOnce::from(&north.neighbors[0]).try_set(&west).unwrap();
188        SetOnce::from(&north.neighbors[1]).try_set(&center).unwrap();
189        SetOnce::from(&north.neighbors[2]).try_set(&east).unwrap();
190
191        SetOnce::from(&west.neighbors[0]).try_set(&north).unwrap();
192        SetOnce::from(&west.neighbors[1]).try_set(&south).unwrap();
193        SetOnce::from(&west.neighbors[2]).try_set(&center).unwrap();
194
195        SetOnce::from(&south.neighbors[0]).try_set(&center).unwrap();
196        SetOnce::from(&south.neighbors[1]).try_set(&west).unwrap();
197        SetOnce::from(&south.neighbors[2]).try_set(&east).unwrap();
198
199        SetOnce::from(&east.neighbors[0]).try_set(&north).unwrap();
200        SetOnce::from(&east.neighbors[1]).try_set(&center).unwrap();
201        SetOnce::from(&east.neighbors[2]).try_set(&south).unwrap();
202
203        vec![center, north, east, south, west]
204    }
205
206    #[test]
207    fn cyclic_graph() {
208        let graph = make_cyclic_graph();
209        let center = graph.first().unwrap();
210
211        assert_eq!(center.value, "Center");
212
213        let north = center.neighbors[0].get();
214        let west = north.neighbors[0].get();
215        let south = west.neighbors[1].get();
216        let east = south.neighbors[2].get();
217        let center_again = east.neighbors[1].get();
218
219        assert_eq!(north.value, "North");
220        assert_eq!(west.value, "West");
221        assert_eq!(south.value, "South");
222        assert_eq!(east.value, "East");
223        assert_eq!(center_again.value, "Center");
224    }
225    #[test]
226    fn duplicate_initialization_fails() {
227        let graph = make_cyclic_graph();
228        let center = graph.first().unwrap();
229
230        let neighbor_slot = &center.neighbors[0];
231        let mutator = SetOnce::from(neighbor_slot);
232        let duplicate_set = mutator.try_set(center);
233
234        assert!(
235            matches!(duplicate_set, Err(DeferredError::DuplicateInitialization())),
236            "Expected DuplicateInitialization error"
237        );
238    }
239    #[test]
240    fn uninitialized_access_fails() {
241        let uninitialized: Deferred<Node> = Deferred::default();
242        let result = uninitialized.try_get();
243
244        assert!(
245            matches!(result, Err(DeferredError::NotInitializedError())),
246            "Expected NotInitializedError"
247        );
248    }
249    #[test]
250    fn iterator_extension_works() {
251        let graph = make_cyclic_graph();
252        let center = graph.first().unwrap();
253
254        let values: Vec<_> = center
255            .neighbors
256            .clone()
257            .into_iter()
258            .get_deferred()
259            .map(|rc| rc.value.clone())
260            .collect();
261
262        assert_eq!(values, vec!["North", "West", "South", "East"]);
263    }
264    #[test]
265    fn deferred_state_checking() {
266        let graph = make_cyclic_graph();
267        let center = graph.first().unwrap();
268        let neighbor = &center.neighbors[0];
269
270        assert!(neighbor.is_ready());
271        let m = SetOnce::from(neighbor);
272        assert!(!m.can_set());
273    }
274}