Crate shared_cell

source ·
Expand description
Interior mutability between concurrent tasks on the same thread

This is essentially an alternative to RefCell without runtime borrow checking. Instead of using borrow guards, uses a closure API inspired by LocalKey::with() for greater guarantees in the asynchronous context (prevents holding onto the mutable reference over an .await point that yields to other tasks that have access to the Cell).

How It Works / Why It’s Safe

A Cell makes it possible to have multiple references data with interior mutability. Being !Sync, it is impossible to call methods on Cell from another thread, preventing data races.

The lifetime of the mutable reference is bound by the closure’s scope, making it impossible to drop() the interior data while borrowed. Since Cell doesn’t let you get an immutable reference to the interior data, not having any existing immutable references is guaranteed, making it safe to construct a mutable reference to pass into the closure.

Taking advantage of the fact that Cell is !Sync, by requiring Sync in the closure provided to CellExt::with(), it is impossible to create a second mutable reference to the data through a reëntrant borrow.

Reëntrant Borrow Prevention Example

use core::cell::Cell;

use shared_cell::CellExt;

struct Context {
    stuff: u32,
}

fn main() {
    let cell = Cell::new(Context { stuff: 42 });

    cell.with(|context| {
        println!("Before: {}", context.stuff);
        context.stuff += 1;
        println!("After: {}", context.stuff);

        // Will not compile
        /* cell.with(|context| {
            context.stuff += 1;
        }); */
    });
}

Structs

  • A set of tasks that run together on the same thread, with shared data.

Traits

  • Cell extension trait for thread-local-inspired API