1 unstable release
0.1.0 | Dec 8, 2022 |
---|
#689 in Concurrency
53KB
718 lines
cell-family
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 FooCellOwner
s 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 wrapsT
in a#[repr(transparent)]
FooCell
without performing any checks.FooCell::get(&FooCellOwner)
andFooCell::get_mut(&mut FooCellOwner)
are constant-time operations that return&T
and&mut T
respectively without performing any runtime checks. Since a singleFooCellOwner
exists per program (or thread), the aliasing rules of each cell is enforced by Rust through theFooCellOwner
, which is borrowed as long as eachFooCell
is borrowed.FooFamily
ensures that a singleFooCellOwner
exists within a program; if anotherFooCellOwner
exists,FooCellOwner::new()
will panic. Atry_new()
counterpart exists to avoid crashing in such a case.
Benefits over qcell::TCell
/ qcell::TLCell
-
Unlike
qcell::TCell
(respectivelyqcell::TCell
), theFamily
F
is in charge of ensuring that a singleCellOwner<F>
exists per program (respectively thread). By using macros to generate families, we only need a singleAtomicBool
(respectivelyCell<bool>
) for each family, thus requiring no allocations. -
A few additional methods are provided; for instance,
owner.get(c)
,owner.get_mut(c)
andowner.try_get_mut(c)
are provided, wherec
can be: -
Thread-local and thread-safe
Cell
s (andCellOwner
s) are backed by the same type; whether they are thread-local or thread-safe is determined by theirFamily
: if it is thread-safe, it will also implementThreadSafeFamily
. This makes it easier to define generic functions overCell
s. -
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
isDebug
, and will print a representation of its inner value if noCellOwner
currently exists.