Crate cell_family

source ·
Expand description

Cells inspired by qcell::TCell / qcell::TLCell, with additional features.

Overview

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:

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:

cell_family::define!(static type FooFamily: FooCellOwner for FooCell<T>);

For both thread-local and thread-safe families, the API is the same:

let mut owner = FooCellOwner::new();
let a = FooCell::new(1);
let b = FooCell::new("bar");

assert_eq!(*a.get(&owner), 1);
assert_eq!(*b.get(&owner), "bar");

*a.get_mut(&mut owner) += 1;
*b.get_mut(&mut owner) = "baz";

assert_eq!(*a.get(&owner), 2);
assert_eq!(*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.

Benefits over qcell::TCell / qcell::TLCell

  • Unlike qcell::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:
    • A tuple of Cells.
    • An array of Cells.
    • An array of references to Cells.
    • A slice of Cells.
  • 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.

Modules

An iterator over Cell values.

Macros

Defines a cell Family, and optional Cell and CellOwner aliases.

Structs

A cell whose mutability is determined through its CellOwner.
Owner of zero or more Cells.

Traits

F marker type in Cell and CellOwner.
Trait implemented by values that can be given to CellOwner::get_mut().
Trait implemented by values that can be given to CellOwner::get().
An extension of Family to define CellOwners that can be shared between different threads, at the cost of slighly more expensive costs to create or drop a CellOwner.