#cell #once-cell #take #value #taken

no-std takecell

A cell type which value can only be taken once

2 releases

0.1.1 Jan 23, 2022
0.1.0 Oct 31, 2021

#581 in Concurrency

Download history 2222/week @ 2023-12-06 2096/week @ 2023-12-13 2580/week @ 2023-12-20 2378/week @ 2023-12-27 2698/week @ 2024-01-03 3040/week @ 2024-01-10 3088/week @ 2024-01-17 2661/week @ 2024-01-24 2307/week @ 2024-01-31 2113/week @ 2024-02-07 2358/week @ 2024-02-14 2564/week @ 2024-02-21 2766/week @ 2024-02-28 2765/week @ 2024-03-06 2548/week @ 2024-03-13 1879/week @ 2024-03-20

10,338 downloads per month
Used in 50 crates (6 directly)

MIT license

23KB
215 lines

takecell

lib.rs docs

takecell provides two new cell-like types, TakeCell and TakeOwnCell. Both may store arbitrary non-Copy types, can be read from at most once and provide direct unique access to the stored contents. The core API looks roughly like this:

impl<T> TakeCell<T> {
    const fn new(v: T) -> Self { ... }
}
impl<T: ?Sized> TakeCell<T> {
    fn take(&self) -> Option<&mut T> { ... }
}

impl<T> TakeOwnCell<T> {
    const fn new(v: T) -> Self { ... }
    fn take(&self) -> Option<T> { ... }
}

To use this crate add the following to your Cargo.toml:

[dependencies] 
takecell = "0.1"

lib.rs:

This crate provides two new cell-like types, TakeCell and TakeOwnCell. Both may store arbitrary non-Copy types, can be read from at most once and provide direct unique access to the stored contents. The core API looks roughly like this (and there’s much more inside, read on!):

impl<T> TakeCell<T> {
    const fn new(v: T) -> Self { ... }
}
impl<T: ?Sized> TakeCell<T> {
    fn take(&self) -> Option<&mut T> { ... }
}

impl<T> TakeOwnCell<T> {
    const fn new(v: T) -> Self { ... }
    fn take(&self) -> Option<T> { ... }
}

Note that, like with RefCell and Mutex, the take method requires only a shared reference. Because of the single read restriction take can return a &mut T or T instead of RefMut<T> or MutexGuard<T>. In some sense TakeCell can be thought as a Mutex without unlocking (or rather with unlocking requiring unique access to the Mutex, see heal).

This crate is #![no_std] and only requires little sychronization via 8-bit atomic.

Usage examples

Singletons

TakeCell is Sync (when T: Sync) and as such it may be used in statics. This can be used to create singletons:

use takecell::TakeCell;

#[non_exhaustive]
pub struct Peripherals {
    pub something: Something,
}

pub static PEREPHERALS: TakeCell<Peripherals> = TakeCell::new(Peripherals {
    something: Something,
});

let peripherals: &'static mut _ = PEREPHERALS.take().unwrap();

Doing work only once

use once_cell::sync::OnceCell;
use std::sync::{Arc, Condvar, Mutex};
use takecell::TakeCell;

#[derive(Clone)]
struct Job {
    // Input can be a type which requires unique access to be used (e.g.: `dyn Read`)
    input: Arc<TakeCell<Input>>,
    output: Arc<OnceCell<Output>>,
    wait: Arc<(Mutex<bool>, Condvar)>,
}

fn execute(job: Job) -> Output {
    match job.input.take() {
        Some(input) => {
            // Nobody has started executing the job yet, so execute it
            let output = input.process();

            // Write the output
            job.output.set(output);

            // Notify other threads that the job is done
            let (lock, cvar) = &*job.wait;
            let mut done = lock.lock().unwrap();
            *done = true;
        }
        None => {
            // Wait for the other thread to do the job
            let (lock, cvar) = &*job.wait;
            let mut done = lock.lock().unwrap();
            // As long as the value inside the `Mutex<bool>` is `false`, we wait
            while !*done {
                done = cvar.wait(done).unwrap();
            }
        }
    }

    // Read the output
    job.output.get().unwrap().clone()
}

impl Input {
    fn process(&mut self) -> Output {
        // ...
    }
}

No runtime deps