Crates.io | token-ref-cell |
lib.rs | token-ref-cell |
version | 0.1.1 |
source | src |
created_at | 2024-08-08 15:06:29.495877 |
updated_at | 2024-08-31 19:36:47.534679 |
description | Interior mutability cell using an external token to synchronize accesses |
homepage | https://github.com/ZettaScaleLabs/token-ref-cell |
repository | https://github.com/ZettaScaleLabs/token-ref-cell |
max_upload_size | |
id | 1329650 |
size | 58,115 |
This library provides TokenRefCell
, an interior mutability cell which uses an
external Token
reference to synchronize its accesses.
Contrary to other standard cells like RefCell
, TokenRefCell
is Sync
as long as its token is Send + Sync
; it can thus be used in multithreaded programs.
Multiple token implementations are provided, the easiest to use being the
smart-pointer-based ones: every Box<T>
can indeed be used as a token (as long as T
is not a ZST). The recommended token implementation is BoxToken
, and it's the
default value of the generic parameter of TokenRefCell
.
The runtime cost is very lightweight: only one pointer comparison for
TokenRefCell::borrow
/TokenRefCell::borrow_mut
when using BoxToken
(and zero-cost when using singleton_token!
).
Because one token can be used with multiple cells, it's possible for example to use
a single rwlock wrapping a token to synchronize mutable access to multiple Arc
data.
use std::sync::{Arc, RwLock};
use token_ref_cell::{TokenRefCell, BoxToken};
let mut token = RwLock::new(BoxToken::new());
// Initialize a vector of arcs
let token_ref = token.read().unwrap();
let arc_vec = vec![Arc::new(TokenRefCell::new(0, &*token_ref)); 42];
drop(token_ref);
// Use only one rwlock to write to all the arcs
let mut token_mut = token.write().unwrap();
for cell in &arc_vec {
*cell.borrow_mut(&mut *token_mut) += 1;
}
drop(token_mut)
Many crates based on the same principle exists: qcell
, ghost-cell
, token-cell
, singleton-cell
, etc.
When I started writing token-ref-cell
, I only knew token-cell
one, as it was written by a guy previously working in the same company as me. But I wanted to take a new approach, notably designing an API closer than standard RefCell
one, hence the name TokenRefCell
. In fact, my goal was simple: to make the graph example compile, and for that I needed to enable "re-borrowing", i.e. reusing the token used in a mutable borrow to mutably borrow a "sub-cell".
When I was satisfied with the result, before publishing it, I search for other similar crates, and I found the list above, and realized I'd reimplemented the same concept as a bunch of people, especially qcell
which uses a Box
for its cell token/owner. However, this fresh implementation still has a few specificities which makes it relevant:
RefCell
one;Token
trait, contrary to qcell
which provides four different cell types;Token
trait: singleton types, smart-pointers, pinned/unpinned references, etc.;no_std
implementation, compatible with custom allocators (doesn't require alloc
crate, and doesn't require allocator at all using RefToken
for example);