odem-rs-core 0.3.0

Core components of the odem-rs simulation framework
Documentation
//! This module provides the wrapper type [`Lease`] that allows extending the
//! lifetime of references using Rust's type system, effectively preventing
//! further access to the original value once a specific borrow pattern is used.
//!
//! This mechanism is crucial for scenarios like intrusive reference counting
//! (`Irc`), where exclusive access rights need to be guaranteed for a pinned,
//! non-movable value without relying on standard ownership transfer.
//!
//! Credit goes to **Lukas Markeffsky** for the idea and proof-of-concept using
//! the underlying mechanism of using an inner lifetime to have the
//! borrow-checker enforce pinning and ownership semantics for mutable borrows.

use core::{
	marker::PhantomData,
	ops::{Deref, DerefMut},
	pin::Pin,
};

/// A wrapper type that leverages Rust's lifetime and variance rules to prevent
/// further access to the wrapped value once a borrow using a specific pattern
/// is taken and consumed.
///
/// `Lease<'p, T>` wraps a value `T` and associates it with an invariant
/// lifetime `'p`. When a borrow like `&'p mut Lease<'p, T>` is passed to a
/// function, the borrow checker prevents the original `Lease` variable from
/// being accessed again for the duration of `'p`. This mimics the exclusivity
/// aspect of ownership transfer without moving the value, which is essential
/// for managing pinned or non-movable data safely.
///
/// ## Examples
/// The primary purpose is to prevent re-borrowing after consumption:
///
/// ```compile_fail,E0499
/// # use odem_rs_core::ptr::{Lease, LeasedMut};
/// // Function consumes the LeasedMut borrow
/// fn consume_lease<T>(_: LeasedMut<'_, T>) {}
///
/// struct S;
/// let mut value = Lease::new(S);
/// // 'p is inferred here, covering the remaining scope of value
/// consume_lease(&mut value); // Consumes the lease for lifetime 'p
///
/// // value is now considered borrowed for its entire remaining lifetime 'p.
/// // Attempting to borrow it again fails:
/// consume_lease(&mut value); // error: value is mutably borrowed
/// ```
///
/// It also prevents moving the `Lease` after a shared borrow (`LeasedRef`)
/// has been taken:
///
/// ```compile_fail,E0505
/// # use odem_rs_core::ptr::{Lease, LeasedRef};
/// fn use_shared_lease<T>(_: LeasedRef<'_, T>) {}
///
/// struct S;
/// let value = Lease::new(S);
/// use_shared_lease(&value); // Borrows value for its remaining lifetime 'p
/// use_shared_lease(&value); // OK: Multiple shared borrows allowed
///
/// // Cannot move value while it's borrowed:
/// let value = value;       // error: value is still borrowed
/// std::mem::forget(value); // error: value is still borrowed
/// ```
///
/// ## Relationship with `Pin`
///
/// `Lease` controls borrow lifetimes but does *not* guarantee that `Drop` will
/// run. If this guarantee is needed (e.g., for `Irc`), `Lease` must be used in
/// conjunction with [`Pin`]. `Pin` provides the [drop guarantee], while `Lease`
/// ensures the borrow checker correctly manages exclusive access rights.
///
/// ```
/// # use {core::pin::{Pin, pin}, odem_rs_core::ptr::{Lease, LeasedMut}};
/// // Function requires a Pinned LeasedMut
/// fn consume_pinned_lease<T>(value: Pin<LeasedMut<'_, T>>) {}
///
/// struct S;
/// impl Drop for S { fn drop(&mut self) { println!("drop runs"); } }
///
/// let mut value = pin!(Lease::new(S)); // Pin the Lease
/// consume_pinned_lease(value.as_mut());
///
/// // Drop is guaranteed to run due to Pin
/// ```
///
/// Without `Pin`, `Drop` could be bypassed using `ManuallyDrop`, potentially
/// breaking safety invariants that rely on destructors running (like `IrcBox`'s
/// drop check).
///
/// To clarify, this code without `Pin` also compiles but doesn't run `Drop`:
/// ```
/// # use {core::mem::ManuallyDrop, odem_rs_core::ptr::{Lease, LeasedMut}};
/// // Function just requires a LeasedMut
/// fn consume_lease<T>(value: LeasedMut<'_, T>) {}
///
/// struct S;
/// impl Drop for S { fn drop(&mut self) { println!("drop runs"); } }
///
/// let mut value = ManuallyDrop::new(Lease::new(S)); // Don't pin the Lease
/// consume_lease(&mut value);
///
/// // Drop does not run (but the address is stable)
/// ```
///
/// # How it works
///
/// `Lease` achieves its borrow-control effect through the interaction of two
/// consequences stemming from its `PhantomData<&'p mut Self>` field:
///
/// 1.  **Inner Lifetime:** `PhantomData<&'p mut Self>` signals to the borrow
///     checker that `Lease<'p, T>` should be treated *as if* it contains a
///     mutable reference tied to the lifetime `'p`. This implies `'p` must be
///     valid for at least the entire duration that the `Lease<'p, T>` instance
///     itself exists, otherwise the phantom inner reference would dangle.
///
/// 2.  **Lifetime Invariance:** This specific `PhantomData` marker makes the
///     lifetime parameter `'p` **invariant** over `Lease<'p, T>`. Variance is
///     explained in the [Rustonomicon]. Invariance prevents the borrow checker
///     from shortening the lifetime `'p` via subtyping coercion when matching
///     types.
///
/// These two constraints work together within the intended usage pattern, such
/// as `LeasedMut<'p, T>` (which is `&'p mut Lease<'p, T>`). When the borrow
/// checker analyzes a borrow like `&'s mut Lease<'p, T>` in this pattern:
///
/// * It knows `'p` must be valid for at least the duration of `Lease`'s
///   existence (from constraint 1).
/// * It knows the `Lease` instance must exist for at least the duration of the
///   borrow `'s` (otherwise `&'s mut` would dangle).
/// * It knows that due to invariance (constraint 2), the inner lifetime `'p`
///   cannot be shortened to match `'s`. Therefore, for the types to be
///   compatible, the borrow lifetime `'s` must be *at least* as long as the
///   required inner lifetime `'p`.
/// * Combining these constraints (`'p` must cover `Lease`, `Lease` must cover
///   `'s`, and `'s` must cover `'p`), the only possibility is that the borrow
///   lifetime `'s` must be exactly equal to the required inner lifetime `'p`.
///
/// This forces the borrow (`&'s mut` which becomes `&'p mut`) to cover the
/// necessary lifetime `'p`. Consequently, once a `LeasedMut<'p, T>` reference
/// is created and passed to a function, the original `Lease` variable binding
/// cannot be accessed again for its remaining lifetime `'p`. This effectively
/// transfers the exclusive access rights away from the original binding for
/// that duration, mimicking ownership transfer without actually moving the
/// value.
///
/// Credit goes to **Lukas Markeffsky** for the idea and proof-of-concept.
///
/// [drop guarantee]: core::pin
/// [Rustonomicon]: https://doc.rust-lang.org/nomicon/subtyping.html#variance
#[derive(Debug, Clone, Copy, Default)]
#[repr(transparent)]
pub struct Lease<'p, T: ?Sized> {
	/// Marker type used to influence variance and borrow checking.
	/// The `&'p mut Self` makes `'p` invariant and links it to the Lease's
	/// own lifetime.
	_mark: PhantomData<&'p mut Self>,
	/// The actual wrapped value of type `T`.
	value: T,
}

/// A type alias representing a shared reference to a [`Lease`] using the
/// pattern `&'p Lease<'p, T>`.
///
/// # Overview
///
/// This pattern ensures that after it is passed, the original `Lease` value
/// cannot be moved or forgotten because it remains borrowed for its lifetime.
///
/// # Example
///
/// ```compile_fail,E0505
/// # use odem_rs_core::ptr::{Lease, LeasedRef};
/// fn ref_it<T>(val: LeasedRef<'_, T>) { /* Borrows val for its lifetime */ }
///
/// struct S;
/// let x = Lease::new(S);
/// ref_it(&x); // 'p inferred, x is borrowed for 'p
/// ref_it(&x); // OK: Multiple shared borrows allowed
/// std::mem::forget(x); // error: value is still borrowed
/// ```
pub type LeasedRef<'p, T> = &'p Lease<'p, T>;

/// A type alias representing a mutable reference to a [`Lease`] using the
/// pattern `&'p mut Lease<'p, T>`.
///
/// # Overview
///
/// This pattern ensures that after it is passed, the original `Lease` value
/// cannot be accessed again (mutably or immutably) for its remaining lifetime,
/// as if ownership had been transferred.
///
/// # Example
///
/// ```compile_fail,E0499
/// # use odem_rs_core::ptr::{Lease, LeasedMut};
/// fn ref_it<T>(val: LeasedMut<'_, T>) { /* Consumes the mutable borrow for its lifetime */ }
///
/// struct S;
/// let mut x = Lease::new(S);
/// ref_it(&mut x); // 'p inferred, consumes borrow for 'p
/// ref_it(&mut x);      // Error: value is still mutably borrowed
/// let read = &x;       // Error: value is still mutably borrowed
/// std::mem::forget(x); // Error: value is still mutably borrowed
/// ```
pub type LeasedMut<'p, T> = &'p mut Lease<'p, T>;

impl<T> Lease<'_, T> {
	/// Creates a new `Lease` wrapping the given `value`.
	pub const fn new(value: T) -> Self {
		Self {
			_mark: PhantomData,
			value,
		}
	}

	/// Consumes the `Lease`, returning the wrapped value.
	///
	/// This method takes ownership of `self` and can only be called before
	/// a reference like [`LeasedRef`] or [`LeasedMut`] has been created
	/// and consumed.
	pub fn into_inner(self) -> T {
		self.value
	}
}

impl<'p, T: ?Sized> Lease<'p, T> {
	/// Projects a pinned `Lease` to a pinned mutable reference to the
	/// underlying `T`.
	///
	/// # Safety
	///
	/// This operation is safe because `Pin` guarantees that the `Lease` (and
	/// thus the `value`) will not be moved, and the `LeasedMut` pattern ensures
	/// this is the only mutable access path.
	pub fn project(self: Pin<&mut Self>) -> Pin<&mut T> {
		// SAFETY: Pin guarantees no move, Lease guarantees unique access.
		// Therefore, projecting the pin to the inner value is safe.
		unsafe { self.map_unchecked_mut(|this| &mut this.value) }
	}

	/// Projects a pinned mutable reference to a `T` into a pinned mutable
	/// reference of a `Lease` of `T`.
	///
	/// # Safety
	///
	/// The caller must ensure that the `pinned_value` is unique, i.e. that
	/// there is no other reference to the `T` that could be used later, after
	/// the `Lease` is created, since that is what the `Lease` signifies.
	pub unsafe fn unchecked_new(pinned_value: Pin<&mut T>) -> Pin<&mut Self> {
		unsafe {
			pinned_value
				.map_unchecked_mut(|inner| core::mem::transmute::<&mut T, &mut Lease<'p, T>>(inner))
		}
	}

	/// Projects the shared reference to a `Lease` from a type to one of its
	/// members.
	///
	/// # Safety
	///
	/// This operation is safe because the lifetimes (`'p`) are correctly
	/// maintained, and the projection relies on the caller-provided function
	/// `f` being sound with respect to pinning. The transmutation is safe
	/// because `Lease` is `#[repr(transparent)]`.
	pub fn map_pin<F, R>(self: Pin<&'p Self>, f: F) -> Pin<&'p Lease<'p, R>>
	where
		F: FnOnce(Pin<&T>) -> Pin<&R>,
	{
		// SAFETY: Pinning guarantees are upheld by projecting via `map_unchecked`.
		// The caller's function `f` must maintain pinning invariants.
		let value = f(unsafe { self.map_unchecked(|inner| &inner.value) });

		// SAFETY: Lease is repr(transparent), so Pin<&R> has the same layout
		// as Pin<&Lease<R>>. The lifetime 'p is carried over correctly.
		unsafe { core::mem::transmute::<Pin<&R>, Pin<LeasedRef<'_, R>>>(value) }
	}

	/// Projects the mutable reference to a `Lease` from a type to one of its
	/// members.
	///
	/// # Safety
	///
	/// This operation is safe because the lifetimes (`'p`) are correctly
	/// maintained, the projection relies on the caller-provided function `f`
	/// being sound with respect to pinning, and `Lease` is
	/// `#[repr(transparent)]`.
	pub fn map_pin_mut<F, R>(self: Pin<&'p mut Self>, f: F) -> Pin<&'p mut Lease<'p, R>>
	where
		F: FnOnce(Pin<&mut T>) -> Pin<&mut R>,
	{
		// SAFETY: Pinning guarantees are upheld by projecting via `map_unchecked_mut`.
		// The caller's function `f` must maintain pinning invariants.
		let value = f(unsafe { self.map_unchecked_mut(|inner| &mut inner.value) });

		// SAFETY: Lease is repr(transparent), so Pin<&mut R> has the same layout
		// as Pin<&mut Lease<R>>. The lifetime 'p is carried over correctly.
		unsafe { core::mem::transmute::<Pin<&mut R>, Pin<LeasedMut<'_, R>>>(value) }
	}
}

impl<T: ?Sized> Deref for Lease<'_, T> {
	type Target = T;

	fn deref(&self) -> &Self::Target {
		&self.value
	}
}

impl<T: ?Sized> DerefMut for Lease<'_, T> {
	fn deref_mut(&mut self) -> &mut Self::Target {
		&mut self.value
	}
}