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}