Crates.io | cell-family |
lib.rs | cell-family |
version | 0.1.0 |
source | src |
created_at | 2022-12-08 13:48:05.008216 |
updated_at | 2022-12-08 13:48:05.008216 |
description | Cheap cells accessed through unique owners |
homepage | |
repository | https://github.com/71/cell-family |
max_upload_size | |
id | 732587 |
size | 66,212 |
cell-family
Cells 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:
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 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::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:
Thread-local and thread-safe Cell
s (and CellOwner
s) 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 Cell
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
is Debug
, and will print a representation of its inner value if no CellOwner
currently exists.