pure_cell/
lib.rs

1// Pure Cell
2// Copyright © 2022 Jeron Aldaron Lau.
3//
4// Licensed under any of:
5// - Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
6// - MIT License (https://mit-license.org/)
7// - Boost Software License, Version 1.0 (https://www.boost.org/LICENSE_1_0.txt)
8// At your choosing (See accompanying files LICENSE_APACHE_2_0.txt,
9// LICENSE_MIT.txt and LICENSE_BOOST_1_0.txt).
10//
11//! Alternative to `GhostCell` that provides safe interior mutability via const
12//! expressions.
13//!
14//! ## Advantages
15//! - Simple, with no cell keys
16//! - Works better with thread local global state.
17//!
18//! ## Disadvantages
19//! - Const closures/fn pointers don't exist (yet), so this crate depends on
20//!   macro magic to sort-of-polyfill them
21//! - Might not always optimize well (TODO)
22//!
23//! Once const contexts support mutable references, this crate will be able to
24//! remove the second disadvantage.  Additionally, once const function pointers
25//! stabilize, this crate will be able to remove the first disadvantage.
26//!
27//! # Getting Started
28//! ```rust
29//! use pure_cell::{PureCell, pure_cell};
30//!
31//! let mut cell = PureCell::new(15);
32//! pure_cell!(cell, (), |state: u32, _args: ()| {
33//!     state += 1;
34//! });
35//! let got = cell.get();
36//! assert_eq!(*got, 16);
37//! ```
38//!
39//! ```rust
40//! use pure_cell::{PureCell, pure_cell};
41//!
42//! let cell = PureCell::new(15);
43//! let amount = 2;
44//! let state = pure_cell!(cell, amount, |state: u32, amount: u32| -> u32 {
45//!     state += amount;
46//!     state
47//! });
48//! assert_eq!(state, 17);
49//! ```
50
51#![no_std]
52#![doc(
53    html_logo_url = "https://ardaku.github.io/mm/logo.svg",
54    html_favicon_url = "https://ardaku.github.io/mm/icon.svg",
55    html_root_url = "https://docs.rs/pure_cell"
56)]
57#![warn(
58    anonymous_parameters,
59    missing_copy_implementations,
60    missing_debug_implementations,
61    missing_docs,
62    nonstandard_style,
63    rust_2018_idioms,
64    single_use_lifetimes,
65    trivial_casts,
66    trivial_numeric_casts,
67    unreachable_pub,
68    unused_extern_crates,
69    unused_qualifications,
70    variant_size_differences
71)]
72
73use core::{cell::UnsafeCell, mem::ManuallyDrop};
74
75/// A cell type that provides interior mutability via "pure" functions.
76#[derive(Debug)]
77pub struct PureCell<T> {
78    value: UnsafeCell<ManuallyDrop<T>>,
79}
80
81impl<T> PureCell<T> {
82    /// Creates a new `PureCell` containing the given value.
83    pub const fn new(value: T) -> Self {
84        Self {
85            value: UnsafeCell::new(ManuallyDrop::new(value)),
86        }
87    }
88
89    /// Returns a mutable reference to the underlying data.
90    pub fn get(&mut self) -> &mut T {
91        self.value.get_mut()
92    }
93
94    /// Update cell.
95    ///
96    /// # Safety
97    /// Sound to use so long as you follow these rules in the closure:
98    ///
99    ///  - Must not yield to other code (usually async)
100    ///  - Must not recursively call `Self::with()`
101    pub unsafe fn with<R, F>(&self, f: F) -> R
102    where
103        F: FnOnce(&mut ManuallyDrop<T>) -> R,
104    {
105        f(&mut *self.value.get())
106    }
107}
108
109impl<T> Drop for PureCell<T> {
110    fn drop(&mut self) {
111        unsafe {
112            let _ = ManuallyDrop::take(&mut *self.value.get());
113        }
114    }
115}
116
117/// Main safe mechanism to mutate [`PureCell`] via a `const` expression.
118#[macro_export]
119macro_rules! pure_cell {
120    (
121        $pure_cell:expr,
122        $input:expr,
123        |$state:ident: $ty:ty, $args:ident: $argty:ty| -> $ret:ty $block:block
124    ) => ({
125        #[inline(always)]
126        const fn const_fn(mut $state: $ty, mut $args: $argty) -> ($ty, $ret) {
127            let (output, state) = ($block, $state);
128            (state, output)
129        }
130        fn wrapper_fn(
131            state: &mut core::mem::ManuallyDrop<$ty>,
132            input: $argty,
133        ) -> $ret {
134            unsafe {
135                let (new, out) = const_fn(
136                    core::mem::ManuallyDrop::take(state),
137                    input,
138                );
139                *state = core::mem::ManuallyDrop::new(new);
140                out
141            }
142        }
143        unsafe {
144            $pure_cell.with(move |state| wrapper_fn(state, $input))
145        }
146    });
147    ($pure_cell:expr, $input:expr, |$state:ident: $ty:ty, $args:ident: $argty:ty| $block:block) => (
148        $crate::pure_cell!($pure_cell, $input, |$state: $ty, $args: $argty| -> () $block)
149    );
150}