cell-familyCells inspired by qcell::TCell / qcell::TLCell, with additional features.
cell-family provides the define! macro, which defines a new Family. For each family, a corresponding Cell and CellOwner can be created. Only a single CellOwner per family can exist at once, but multiple cells can exist at the same time.
For instance, you may define a family FooFamily as below:
rust
cell_family::define!(type FooFamily: FooCellOwner for FooCell<T>);
This defines FooFamily (which implements Family) as well as FooCellOwner and FooCell, aliases for CellOwner<FooFamily> and Cell<FooFamily> respectively.
One FooCellOwner can exist per thread, and thus FooCellOwner is not Send, since sending a FooCellOwner to another thread may allow two FooCellOwners to co-exist in a single thread. To allow a single FooCellOwner per program (and thus make FooCellOwner Send), prefix define! with static:
rust
cell_family::define!(static type FooFamily: FooCellOwner for FooCell<T>);
For both thread-local and thread-safe families, the API is the same:
```rust let mut owner = FooCellOwner::new(); let a = FooCell::new(1); let b = FooCell::new("bar");
asserteq!(*a.get(&owner), 1); asserteq!(*b.get(&owner), "bar");
*a.getmut(&mut owner) += 1; *b.getmut(&mut owner) = "baz";
asserteq!(*a.get(&owner), 2); asserteq!(*b.get(&owner), "baz"); ```
FooCell::new(T) simple wraps T in a #[repr(transparent)] FooCell without performing any checks.FooCell::get(&FooCellOwner) and FooCell::get_mut(&mut FooCellOwner) are constant-time operations that return &T and &mut T respectively without performing any runtime checks. Since a single FooCellOwner exists per program (or thread), the aliasing rules of each cell is enforced by Rust through the FooCellOwner, which is borrowed as long as each FooCell is borrowed.FooFamily ensures that a single FooCellOwner exists within a program; if another FooCellOwner exists, FooCellOwner::new() will panic. A try_new() counterpart exists to avoid crashing in such a case.qcell::TCell / qcell::TLCellqcell::TCell (respectively qcell::TCell), the Family F is in charge of ensuring that a single CellOwner<F> exists per program (respectively thread). By using macros to generate families, we only need a single AtomicBool (respectively Cell<bool>) for each family, thus requiring no allocations.A few additional methods are provided; for instance, owner.get(c), owner.get_mut(c) and owner.try_get_mut(c) are provided, where c can be:
Thread-local and thread-safe Cells (and CellOwners) are backed by the same type; whether they are thread-local or thread-safe is determined by their Family: if it is thread-safe, it will also implement ThreadSafeFamily. This makes it easier to define generic functions over Cells.
cell-family fully supports #[no_std], except for thread-local families in non-nightly builds (since thread-local variables cannot be defined in #[no_std] without #[thread_local], which is not stable).Cell is Debug, and will print a representation of its inner value if no CellOwner currently exists.