toast_cell/
lib.rs

1//! A type-branded cell for aliasing checked at compile-time.
2//!
3//! It's based on the prior work of [GhostCell], but trades lifetime brands for
4//! type brands.
5//!
6//! [GhostCell]: https://plv.mpi-sws.org/rustbelt/ghostcell/
7//!
8//! # Usage
9//!
10//! The interface of this crate is very similar to that of GhostCell. The main
11//! difference is that "tokens" are type brands from
12//! [`type-factory`](type_factory) instead of lifetime brands.
13//!
14//! ```
15//! use toast_cell::{type_factory, Cell};
16//!
17//! // Create a unique token.
18//! type_factory::with(|mut token| {
19//!     let cell = Cell::new(0);
20//!
21//!     // These are all references to the same cell, but we can still mutate it safely!
22//!     let references = [&cell, &cell, &cell];
23//!     for cell in references {
24//!         *cell.borrow_mut(&mut token) += 1;
25//!     }
26//!
27//!     assert_eq!(cell.into_inner(), 3);
28//! });
29//! ```
30//!
31//! As <code>impl [Unique]</code> brand types cannot be copied and are
32//! exclusively borrowed for mutation, [`Cell`]s of the same brand are
33//! guaranteed to never alias.
34//!
35//! ```compile_fail,E0499
36//! # use toast_cell::{type_factory, Cell};
37//! type_factory::with(|mut token| {
38//!     let cell = Cell::new("Ghost");
39//!     let borrow1 = cell.borrow_mut(&mut token);
40//!     let borrow2 = cell.borrow_mut(&mut token);
41//!
42//!     // error: cannot borrow `token` as mutable more than once at a time
43//!     *borrow1 = "Toast";
44//! });
45//! ```
46//!
47//! It is also guaranteed that cells used with one brand are incompatible with
48//! other brands.
49//!
50//! ```compile_fail,E0308
51//! # use toast_cell::{type_factory, Cell};
52//! type_factory::with(|token| {
53//!     let (mut token, mut other_token) = type_factory::split(token);
54//!
55//!     let cell = Cell::new(41);
56//!     *cell.borrow_mut(&mut token) += 1;       // ✅
57//!     *cell.borrow_mut(&mut other_token) -= 1; // ❌
58//! });
59//! ```
60//!
61//! # How does it work?
62//!
63//! The documentation of this crate is currently rather sparse. I recommend
64//! reading the documentation of
65//! [`ghost-cell`](https://crates.io/crates/ghost-cell) for more information on
66//! the exact properties of GhostCell.
67//!
68//! # Safety
69//!
70//! Unlike GhostCell, this crate has not been formally proven. Use at your own
71//! risk.
72//!
73//! # Minimum supported Rust version
74//!
75//! The MSRV is currently 1.83.
76//!
77//! This may change between minor releases.
78//!
79//! # License
80//!
81//! This crate is licensed under the
82//! [Blue Oak Model License 1.0.0](https://blueoakcouncil.org/license/1.0.0).
83
84// Attributes
85#![cfg_attr(not(any(doc, test)), no_std)]
86// Lints
87#![deny(
88	clippy::multiple_unsafe_ops_per_block,
89	clippy::undocumented_unsafe_blocks,
90	unsafe_op_in_unsafe_fn
91)]
92
93use {
94	core::{cell::UnsafeCell, marker::PhantomData, mem},
95	type_factory::Unique,
96};
97
98#[cfg(any(doctest, test))]
99mod tests;
100
101pub use type_factory;
102
103#[repr(transparent)]
104pub struct Cell<T, B>
105where
106	T: ?Sized,
107	B: Unique,
108{
109	marker: PhantomData<fn() -> B>,
110	inner: UnsafeCell<T>,
111}
112
113impl<T, B> Cell<T, B>
114where
115	B: Unique,
116{
117	pub const fn new(value: T) -> Self {
118		Self {
119			marker: PhantomData,
120			inner: UnsafeCell::new(value),
121		}
122	}
123
124	pub fn into_inner(self) -> T {
125		self.inner.into_inner()
126	}
127
128	pub fn take(&self, token: &mut B) -> T
129	where
130		T: Default,
131	{
132		mem::take(self.borrow_mut(token))
133	}
134
135	pub const fn replace(&self, value: T, token: &mut B) -> T {
136		mem::replace(self.borrow_mut(token), value)
137	}
138}
139
140impl<T, B> Cell<T, B>
141where
142	T: ?Sized,
143	B: Unique,
144{
145	pub const fn from_mut(value: &mut T) -> &mut Self {
146		// SAFETY: `Cell<T, B>` is `repr(transparent)` over `T`.
147		unsafe { &mut *(value as *mut T as *mut Self) }
148	}
149
150	pub const fn get_mut(&mut self) -> &mut T {
151		self.inner.get_mut()
152	}
153
154	pub const fn as_ptr(&self) -> *mut T {
155		self.inner.get()
156	}
157
158	pub const fn borrow<'a>(&'a self, #[allow(unused_variables)] token: &'a B) -> &'a T {
159		// SAFETY: The token is immutably borrowed so there is no aliasing.
160		unsafe { &*self.inner.get() }
161	}
162
163	pub const fn borrow_mut<'a>(
164		&'a self,
165		#[allow(unused_variables)] token: &'a mut B,
166	) -> &'a mut T {
167		// SAFETY: The token is mutably borrowed so there is no aliasing.
168		unsafe { &mut *self.inner.get() }
169	}
170}
171
172impl<T, B> AsMut<T> for Cell<T, B>
173where
174	T: ?Sized,
175	B: Unique,
176{
177	fn as_mut(&mut self) -> &mut T {
178		self.get_mut()
179	}
180}
181
182impl<T, B> Default for Cell<T, B>
183where
184	T: Default,
185	B: Unique,
186{
187	fn default() -> Self {
188		Self::new(T::default())
189	}
190}
191
192impl<T, B> From<T> for Cell<T, B>
193where
194	B: Unique,
195{
196	fn from(value: T) -> Self {
197		Self::new(value)
198	}
199}
200
201// SAFETY: `Cell<T>` owns `T`, so it can be sent across threads if `T` can.
202unsafe impl<T, B> Send for Cell<T, B>
203where
204	T: ?Sized + Send,
205	B: Unique,
206{
207}
208
209// SAFETY: `Cell<T>` owns `T`, so it can be accessed from different threads
210//         if `T` can. `T` must also be `Send` as it can be extracted from
211//         methods such as `Cell::replace`
212unsafe impl<T, B> Sync for Cell<T, B>
213where
214	T: ?Sized + Send + Sync,
215	B: Unique,
216{
217}