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.
Weak References (feature weak)
Enable the optional weak feature to get non-owning Weak<T> handles. A weak handle does not keep the value alive; the value is dropped when the last strong ThinCell is dropped, and the allocation is freed once the last Weak is dropped. Internally, the strong side holds one implicit weak reference like Rc/Arc. Use ThinCell::downgrade to create a Weak<T> and Weak::upgrade to try to regain a strong handle. Both sync and unsync versions expose strong_count and weak_count when the feature is on.
#
#
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