A single-assignment, weak reference wrapper for cyclic node graphs with write-once, late initialization.
This crate provides a lightweight alternative to RefCell<Option<Weak<T>>> when building write-once reference graphs, such as cyclic trees, linked lists, and bidirectional or circular structures. It enables you to cleanly and safely establish weak links between nodes after they are constructed.
- ✅ Write-once semantics using
OnceCell<Weak<T>> - ✅ Safe and ergonomic API for deferred initialization
- ✅ Strong test coverage
- ✅ Optional helper type for setting values:
SetOnce - ✅ Iterator extension trait for working with collections
Add to your Cargo.toml:
[dependencies]
deferred-cell = "0.6"In Rust, it’s often tricky to build cyclic data structures due to ownership rules. A common workaround is:
RefCell<Option<Weak<T>>>However, this allows re-assignment and mutation, which is overkill in cases where the weak reference should be set only once. deferred-cell simplifies this pattern with explicit, single-assignment behavior and no runtime borrow checking.
use deferred_cell::{Deferred, SetOnce, DeferredError};
use std::rc::Rc;
struct Node {
value: String,
neighbor: Deferred<Node>,
}
fn main() -> Result<(), DeferredError> {
let node = Rc::new(Node {
value: "A".into(),
neighbor: Deferred::default(),
});
let neighbor = Rc::new(Node {
value: "B".into(),
neighbor: Deferred::default(),
});
// Assign weak reference after both nodes are constructed
SetOnce::from(&node.neighbor).try_set(&neighbor)?;
// Access the neighbor
let linked = node.neighbor.try_get()?;
assert_eq!(linked.value, "B");
Ok(())
}let d: Deferred<T> = Deferred::default();
let rc: Rc<T> = ...;
// One-time set
SetOnce::from(&d).try_set(&rc)?;
// Later access
let strong: Rc<T> = d.try_get()?;
// Optional checks
if d.is_ready() { ... }Also includes a DeferredIteratorExt trait to streamline iteration:
let values: Vec<_> = list
.into_iter()
.get_deferred()
.map(|rc| rc.value.clone())
.collect();Two error types are defined:
DeferredError::DuplicateInitialization– iftry_set()is called more than onceDeferredError::NotInitializedError– ifget()ortry_get()is called before a value is set