toast-cell 0.1.1

GhostCell implementation using types rather than lifetimes
Documentation
//! # `toast_cell`
//!
//! An alternate implementation of [GhostCell] using types rather than lifetimes
//! for its brands.
//!
//! [GhostCell]: https://plv.mpi-sws.org/rustbelt/ghostcell/
//!
//! ## Safety
//!
//! Unlike GhostCell, this crate has not been formally proven. Use at your own
//! risk.
//!
//! ## Usage
//!
//! The interface of this crate is very similar to that of GhostCell. The main
//! difference is that [`GhostToken`]s do not require a closure to create a
//! unique invariant lifetime.
//!
//! ```
//! use toast_cell::{type_factory::unique, GhostCell, GhostToken};
//!
//! // 1. Create a brand type.
//! let brand = unique!();
//!
//! // 2. Make a unique `GhostToken` out of it.
//! //    The brand is consumed, so it cannot be used by any other token.
//! let mut token = GhostToken::new(brand);
//!
//! // 3. Use it with some `GhostCell`s.
//! let cell = GhostCell::new(0);
//! let references = [&cell, &cell, &cell];
//! for cell in references {
//!     *cell.borrow_mut(&mut token) += 1;
//! }
//!
//! assert_eq!(cell.into_inner(), 3);
//! ```
//!
//! As the <code>impl [Unique]</code> brand types generated cannot be copied and
//! are consumed, each `GhostToken` is guaranteed to be unique from any other.
//!
//! The brand types also guarantee that [`GhostCell`]s used with one token are
//! incompatible with other tokens.
//!
//! ```compile_fail,E0308
//! # use toast_cell::{type_factory::unique, GhostCell, GhostToken};
//! let mut token = GhostToken::new(unique!());
//! let cell = GhostCell::new(41);
//! *cell.borrow_mut(&mut token) += 1;       // ✅
//!
//! let mut other_token = GhostToken::new(unique!());
//! *cell.borrow_mut(&mut other_token) -= 1; // ❌
//! ```
//!
//! ## How does it work?
//!
//! The documentation of this crate is currently rather sparse. I would
//! recommend seeing the documentation of the [`ghost-cell` crate][ghost_cell]
//! for more information on the exact properties of GhostCell.
//!
//! As for the brand types, this crate uses the
//! [`type-factory` crate](type_factory) to generate unique types to use as
//! brands.
//!
//! [ghost_cell]: https://crates.io/crates/ghost-cell
//!
//! ## Minimum supported Rust version
//!
//! The MSRV is currently 1.61.
//!
//! This may change between minor versions.
//!
//! ## Similar crates
//!
//! There are many different crates intended to implement statically-checked
//! mutability like this. Ignoring exclusively runtime-checked implementations,
//! the following are the most notable examples:
//!
//! - [`ghost-cell`][ghost_cell] is the inspiration of this crate. It uses
//!   lifetime-branded tokens. Tokens and their associated cells may only exist
//!   within the scope of their containing closure.
//! - [`qcell`][qcell] provides two cells that are at least partially
//!   statically-checked:
//!   - `LCell` uses a lifetime-branded `LCellOwner`. It may be created within a
//!     scope like `ghost-cell` or constructed with
//!     [`generativity`][generativity].
//!   - `TCell` uses a type-branded `TCellOwner`. Unlike this crate, it uses a
//!     user-provided type, which means that it cannot guarantee uniqueness. It
//!     panics at runtime if the user attempts to construct a duplicate
//!     `TCellOwner`.
//! - [`frankencell`][frankencell] generates unique tokens using `const`-generic
//!   `usize` IDs. They are created using an incrementing `TokenBuilder<N>`.
//!   The first builder, `TokenBuilder<0>`, can only be created once. This is
//!   runtime-checked.
//! - [`singleton-cell`][singleton_cell] provides a lifetime-branded token like
//!   `ghost-cell` in addition to singleton token types generated
//!   by a macro. Like `frankencell`, each may only be instantiated once, and
//!   this is runtime-checked.
//!
//! The way this crate is unique is that:
//!
//! - It does not rely on lifetimes, lifting related restrictions.
//! - The brand types are guaranteed to be unique and do not require runtime
//!   checks.
//!
//! [ghost_cell]: https://crates.io/crates/ghost-cell
//! [qcell]: https://crates.io/crates/qcell
//! [generativity]: https://crates.io/crates/generativity
//! [frankencell]: https://crates.io/crates/frankencell
//! [singleton_cell]: https://crates.io/crates/singleton-cell

// Attributes
#![cfg_attr(not(any(doc, test)), no_std)]
// Lints
#![warn(missing_docs)]
#![deny(
	clippy::multiple_unsafe_ops_per_block,
	clippy::undocumented_unsafe_blocks,
	unsafe_op_in_unsafe_fn
)]

use {
	core::{cell::UnsafeCell, marker::PhantomData, mem},
	type_factory::Unique,
};

#[cfg(any(doctest, test))]
mod tests;

pub use type_factory;

pub struct GhostToken<B>
where
	B: Unique,
{
	marker: PhantomData<fn(B) -> B>,
}

impl<B> GhostToken<B>
where
	B: Unique,
{
	pub const fn new(brand: B) -> Self {
		// This hack lets the function be `const`.
		// It's okay because `impl Unique` types do nothing when dropped.
		mem::forget(brand);

		Self {
			marker: PhantomData,
		}
	}
}

#[repr(transparent)]
pub struct GhostCell<T, B>
where
	T: ?Sized,
	B: Unique,
{
	marker: PhantomData<fn(B) -> B>,
	inner: UnsafeCell<T>,
}

impl<T, B> GhostCell<T, B>
where
	B: Unique,
{
	pub const fn new(value: T) -> Self {
		Self {
			marker: PhantomData,
			inner: UnsafeCell::new(value),
		}
	}

	pub fn into_inner(self) -> T {
		self.inner.into_inner()
	}

	pub fn take(&self, token: &mut GhostToken<B>) -> T
	where
		T: Default,
	{
		mem::take(self.borrow_mut(token))
	}

	pub fn replace(&self, value: T, token: &mut GhostToken<B>) -> T {
		mem::replace(self.borrow_mut(token), value)
	}
}

impl<T, B> GhostCell<T, B>
where
	T: ?Sized,
	B: Unique,
{
	pub fn from_mut(value: &mut T) -> &mut Self {
		// SAFETY: `GhostCell<T, B>` is `repr(transparent)` over `T`.
		unsafe { &mut *(value as *mut T as *mut Self) }
	}

	pub fn get_mut(&mut self) -> &mut T {
		self.inner.get_mut()
	}

	pub const fn as_ptr(&self) -> *mut T {
		self.inner.get()
	}

	pub fn borrow<'a>(&'a self, #[allow(unused_variables)] token: &'a GhostToken<B>) -> &'a T {
		// SAFETY: The token is immutably borrowed so there is no aliasing.
		unsafe { &*self.inner.get() }
	}

	pub fn borrow_mut<'a>(
		&'a self,
		#[allow(unused_variables)] token: &'a mut GhostToken<B>,
	) -> &'a mut T {
		// SAFETY: The token is mutably borrowed so there is no aliasing.
		unsafe { &mut *self.inner.get() }
	}
}

impl<T, B> AsMut<T> for GhostCell<T, B>
where
	T: ?Sized,
	B: Unique,
{
	fn as_mut(&mut self) -> &mut T {
		self.get_mut()
	}
}

impl<T, B> Default for GhostCell<T, B>
where
	T: Default,
	B: Unique,
{
	fn default() -> Self {
		Self::new(T::default())
	}
}

impl<T, B> From<T> for GhostCell<T, B>
where
	B: Unique,
{
	fn from(value: T) -> Self {
		Self::new(value)
	}
}

// SAFETY: `GhostCell<T>` owns `T`, so it can be sent across threads if `T` can.
unsafe impl<T, B> Send for GhostCell<T, B>
where
	T: ?Sized + Send,
	B: Unique,
{
}

// SAFETY: `GhostCell<T>` owns `T`, so it can be accessed from different threads
//         if `T` can. `T` must also be `Send` as it can be extracted from
//         methods such as `GhostCell::replace`
unsafe impl<T, B> Sync for GhostCell<T, B>
where
	T: ?Sized + Send + Sync,
	B: Unique,
{
}