Giter Club home page Giter Club logo

modalcell's Introduction

ModalCell

crate version Live Build Status Documentation for main branch

This crate is considered experimental. It contains unsafe code that the author is fairly confident in, but the crate author is seeking feedback on its approach to safety.

ModalCell provides an approach for using Rust's borrow checker to enforce access to a value via an associated mode. This crate decouples permissions from data, but unlike many existing GhostCell-like crates, this crate splits permissions into two modes: exclusive access and shared.

Consider this example:

use modalcell::{ExclusiveCell, ExclusiveMode, RefMut, SharedCell, SharedMode};

// Create our shared mode.
let mut shared = SharedMode::new();
// Obtain exclusvie access to the mode.
let exclusive: ExclusiveMode<'_, _> = shared.as_exclusive();
// Create a new cell with the initial value of `1`. This cell can be
// accessed `mut`-ably using an `ExclusiveMode`.
let mut exclusive_cell: ExclusiveCell<usize, _> = exclusive.new_cell(1);
// Obtain a SharedCell, which can be converted to a `&T` using a
// `SharedMode`.
let shared_cell: SharedCell<usize, _> = exclusive_cell.as_shared();

// The Rust compiler now guarantees safe access to the data.
// shared_cell.get(&shared); // Because `exclusive` borrowed from
//                           // `shared`, this is a compilation error.

let cell_contents: &usize = shared_cell.get(&shared);
assert_eq!(*cell_contents, 1);

// To change the value, we must obtain exclusive access again.
let exclusive: ExclusiveMode<'_, _> = shared.as_exclusive();

// `&T` references prevent entering exclusive mode. This can be tested by
// uncommenting the following line:
// assert_eq!(*cell_contents, 1);

// Each RefMut tracks the lifetime of the &mut ExclusiveCell<T> as well as
// the lifetime of the `ExclusiveMode`. This ensures that no `&mut T` can be
// created without exclusive access to `shared`.
let mut cell_contents: RefMut<'_, usize, _> = exclusive_cell.get_mut(exclusive);
*cell_contents = 2;

assert_eq!(*shared_cell.get(&shared), 2);

// Accessing `shared_cell` can only be done safely if `cell_contents` isn't
// used again. Uncommenting this line will produce a compiler error.
// *cell_contents = 3;

The approach taken by this crate gives similar properties to an Rc<RefCell<T>>, except that with an Rc<RefCell<T>> the contents must be accessed through std::cell::Ref/std::cell::RefMut. This crate allows direct access to shared references without an intermediate type. With this type, the Rust compiler enforces correctness, while RefCell requires runtime checks.

This crate still performs one check at runtime: is the mode being passed the same one that was used to create the cell. Because this is a logic bug, passing an incorrect mode will result in a panic.

This crate also provides an implementation that allows using this mode of data access in multi-threaded code:

use std::sync::mpsc::{Receiver, SyncSender};

use modalcell::threadsafe::{ExclusiveCell, SharedMode};

fn main() {
    let mut shared = SharedMode::new_threadsafe();
    let cell = shared.new_cell(0);
    let shared_cell = cell.as_shared();

    let (counting_sender, counting_receiver) = std::sync::mpsc::sync_channel(1);
    let (printing_sender, printing_receiver) = std::sync::mpsc::sync_channel(1);

    // Spawn a thread that updates the value.
    std::thread::spawn(|| counting_thread(cell, counting_receiver, printing_sender));

    loop {
        // Send the mode to the thread, allowing it to gain mutable access.
        counting_sender.send(shared).unwrap();
        // Wait for mode to be returned to us.
        shared = printing_receiver.recv().unwrap();
        // Use it to gain access a reference of the value.
        let value: &usize = shared_cell.get(&shared);
        println!("New Count: {value}");
        // Stop after 10.
        if *value == 10 {
            break;
        }
    }
}

fn counting_thread(
    mut cell: ExclusiveCell<usize>,
    receiver: Receiver<SharedMode>,
    sender: SyncSender<SharedMode>,
) {
    while let Ok(mut shared) = receiver.recv() {
        // Enter exclusive mode. This is a borrow-checker only operation and has
        // no runtime overhead.
        let exclusive = shared.as_exclusive();
        // Gain access to the cell's contents using our exclusive marker.
        let mut contents = cell.get_mut(exclusive);
        // Update the value through DerefMut.
        *contents += 1;
        // Return the `SharedMode` back to the other thread.
        sender.send(shared).unwrap();

        // Attempting to use `exclusive` now will result in a compiler error.
        // let _error = cell.get_mut(exclusive);
        // let _error = *contents;
    }
}

What is the use case for this?

Consider an application where data access is split into two phases: creation/update and read-only. During the creation/update phase, read-only references (SharedCell<T>) to the underlying data (ExclusiveCell<T>) can be placed in a structure that is passed onto the read-only phase of the application. When the underlying data needs to be updated, control can be passed back to the creation/update phase.

This approach would normally require Rc<RefCell<T>> or Arc<Mutex<T>> and incur runtime overhead to ensure that no safety constraints are violated. This crate offers an approach that removes nearly all runtime checks by proving the code is safe with the Rust compiler.

modalcell's People

Contributors

ecton avatar

Watchers

 avatar  avatar

Forkers

asonix

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.