thin-cell
A compact smart pointer combining reference counting and interior mutability.
ThinCell is a space-efficient alternative to Rc<RefCell<T>> or Arc<Mutex<T>> that itself is always 1 pointer-sized no matter if T is Sized or not (like ThinBox), compare to Rc<RefCell<T>> which is 2 pointer-sized for T: !Sized.
Features
- One-
usizepointer, no matter whatTis - Reference counted ownership (like
RcorArc) - Interior mutability with only mutable borrows (so it only needs 1-bit to track borrow state)
- Both
syncandunsyncversions, with the same API and slightly different behavior on borrow rules (see below)
How It Works
ThinCell achieves its compact representation by storing metadata inline at offset 0 of the allocation (for unsized types) like ThinBox does.
Simplified layout:
Borrow Semantics
Unlike RefCell which supports multiple immutable borrows OR one mutable borrow, ThinCell only supports one mutable borrow at a time. Attempting to borrow while already borrowed will:
sync::ThinCell<T>: Block current thread by busy-looping and yield to other threads;unsync::ThinCell<T>: Panic immediately.
try_borrow is available for both versions, which returns None instead of panicking or blocking when already borrowed.
Examples
Basic Usage
# use ThinCell;
let cell = new;
// Clone to create multiple owners
let cell2 = cell.clone;
// Borrow mutably
// borrow is released here
// Access from another owner
assert_eq!;
With Trait Objects (Unsized Types)
Due to limitation of stable rust, or in particular, the lack of CoerceUnsized, creating a ThinCell<dyn Trait> from a concrete type requires manual coercion, and that coercion's safety has to be guaranteed by the user. Normally just ptr as *const Inner<MyUnsizedType> or ptr as _ with external type annotation is good enough:
# use ThinCell;
;
// Create a ThinCell<dyn Animal> from a concrete type
// Or you can write `unsafe { ThinCell::new_unsize(Dog, |p| p as *const Inner<dyn Animal>) };`
let cell: = unsafe ;
// Still only 1 word of storage!
assert_eq!;
Borrow Checking
use ThinCell;
let cell = new;
let borrow1 = cell.borrow;
let borrow2 = cell.borrow; // Panics! Already borrowed
Use try_borrow for non-panicking behavior:
# use ThinCell;
let cell = new;
let borrow1 = cell.borrow;
assert!; // Returns None instead of panicking