thin-cell 0.2.1

A compact, single-threaded smart pointer combining reference counting and interior mutability
Documentation
# thin-cell

[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/compio-rs/thin-cell/blob/master/LICENSE)
[![crates.io](https://img.shields.io/crates/v/thin-cell)](https://crates.io/crates/thin-cell)
[![docs.rs](https://img.shields.io/badge/docs.rs-thin--cell-latest)](https://docs.rs/thin-cell)
[![Check](https://github.com/compio-rs/thin-cell/actions/workflows/ci_check.yml/badge.svg)](https://github.com/compio-rs/thin-cell/actions/workflows/ci_check.yml)
[![Test](https://github.com/compio-rs/thin-cell/actions/workflows/ci_test.yml/badge.svg)](https://github.com/compio-rs/thin-cell/actions/workflows/ci_test.yml)
[![Telegram](https://img.shields.io/badge/Telegram-compio--rs-blue?logo=telegram)](https://t.me/compio_rs)

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-`usize` pointer, no matter what `T` is
- Reference counted ownership (like `Rc` or `Arc`)
- Interior mutability with only mutable borrows (so it only needs 1-bit to
  track borrow state)
- Both `sync` and `unsync` versions, 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:

```rust,ignore
struct Inner<T> {
    metadata: usize,
    state: usize
    data: T,
}
```

## 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.

```rust
# #[cfg(feature = "weak")]
# {
use thin_cell::sync::ThinCell;

let cell = ThinCell::new(String::from("hello"));
let weak = cell.downgrade();

assert_eq!(cell.strong_count(), 1);
assert_eq!(cell.weak_count(), 2);

let strong = weak.upgrade().unwrap();
assert_eq!(strong.strong_count(), 2);

drop(cell);
drop(strong);
assert!(weak.upgrade().is_none()); // value already dropped
# }
```

## Examples

### Basic Usage

```rust
# use thin_cell::unsync::ThinCell;
let cell = ThinCell::new(42);

// Clone to create multiple owners
let cell2 = cell.clone();

// Borrow mutably
{
    let mut borrowed = cell.borrow();
    *borrowed = 100;
} // borrow is released here

// Access from another owner
assert_eq!(*cell2.borrow(), 100);
```

### With Trait Objects (Unsized Types)

Due to limitation of stable rust, or in particular, the lack of [`CoerceUnsized`](https://doc.rust-lang.org/std/ops/trait.CoerceUnsized.html), creating a `ThinCell<dyn Trait>` from a concrete type requires manual [`coercion`](https://doc.rust-lang.org/reference/type-coercions.html#unsized-coercions), 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:

```rust
# use thin_cell::unsync::ThinCell;
trait Animal {
    fn speak(&self) -> &str;
}

struct Dog;

impl Animal for Dog {
    fn speak(&self) -> &str {
        "Woof!"
    }
}

// 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: ThinCell<dyn Animal> = unsafe { ThinCell::new_unsize(Dog, |p| p as _) };

// Still only 1 word of storage!
assert_eq!(std::mem::size_of_val(&cell), std::mem::size_of::<usize>());
```

### Borrow Checking

```rust,should_panic
use thin_cell::unsync::ThinCell;

let cell = ThinCell::new(42);

let borrow1 = cell.borrow();
let borrow2 = cell.borrow(); // Panics! Already borrowed
```

Use `try_borrow` for non-panicking behavior:

```rust
# use thin_cell::unsync::ThinCell;
let cell = ThinCell::new(42);

let borrow1 = cell.borrow();
assert!(cell.try_borrow().is_none()); // Returns None instead of panicking
```