hv_elastic/
lib.rs

1//! # Heavy Elastic - almost-safe abstractions for "stretching" lifetimes (and dealing with what
2//! happens when they have to snap back.)
3//!
4//! This crate provides six main abstractions:
5//! - [`Stretchable<'a>`], a trait indicating that some type with a lifetime `'a` can have that
6//!   lifetime `'a` *removed* (and virtually set to `'static`.)
7//! - [`Stretched`], an unsafe trait denoting that a type is a stretched version of a
8//!   [`Stretchable<'a>`] type.
9//! - [`Elastic<T>`], a `'static` "slot" which can be temporarily loaned a non-`'static` value.
10//! - [`ScopeGuard`], a collection allocated in a [`ScopeArena`] which gathers [`ElasticGuard`]s and
11//!   ensures that they are not mishandled.
12//! - [`ScopeArena`], an arena allocator specialized for safely allocating [`ScopeGuard`]s.
13//! - [`ElasticGuard<'a, T>`], a guard which takes on the lifetime of loans to an [`Elastic`] and
14//!   handles expiring the loans.
15//!
16//! Requires nightly for `#![feature(generic_associated_types, allocator_api)]`.
17//!
18//! # Why would I want this?
19//!
20//! `Elastic` excels at "re-loaning" objects across "`'static` boundaries" and "`Send` boundaries".
21//! For example, the Rust standard library's [`std::thread::spawn`] function requires that the
22//! `FnOnce` closure you use to start a new thread is `Send + 'static`. I'm calling this a
23//! "`'static` boundary" because it effectively partitions your code into two different sets of
24//! lifetimes - the lifetimes on the parent thread, and the lifetimes on the child thread, and
25//! you're forced to separate these because of a `'static` bound on the closure. So what happens if
26//! you need to send over a reference to something which isn't `'static`? Without unsafe
27//! abstractions or refactoring to remove the lifetime (which in some cases won't be possible
28//! because the type isn't from your crate in the first place) you're, generally speaking, screwed.
29//! `Elastic` lets you get around this problem by providing a "slot" which can have a value safely
30//! and remotely loaned to it.
31//!
32//! ## Using `Elastic` for crossing `Send + 'static` boundaries
33//!
34//! Let's first look at the problem without `Elastic`:
35//!
36//! ```compile_fail
37//! # use core::cell::RefCell;
38//! let my_special_u32 = RefCell::new(23);
39//!
40//! // This fails for two reasons: first, RefCell is not Sync, so it's unsafe to Send any kind
41//! // of reference to it across a thread boundary. Second, the `&'a mut RefCell<u32>` created
42//! // implicitly by this closure borrowing its environment is non-static.
43//! std::thread::spawn(|| {
44//!     *my_special_u32.borrow_mut() *= 3;
45//! }).join().unwrap();
46//!
47//! // We know that the thread will have returned by now thanks to the use of `.join()`, but
48//! // the borrowchecker has no way of proving that! Which is why it requires the closure to be
49//! // static in the first place.
50//! let important_computed_value = *my_special_u32.borrow() * 6 + 6;
51//! ```
52//!
53//! If you're stuck with a [`RefCell<T>`], it may be hard to see a way to get a mutable reference to
54//! its contained value across a combined `'static + Send` boundary. However, with [`Elastic`], you
55//! can deal with the situation cleanly, no matter where the `&'a mut T` comes from:
56//!
57//! ```
58//! # use hv_elastic::{ElasticMut, ScopeArena};
59//! # use core::cell::RefCell;
60//! # let my_special_u32 = RefCell::new(23);
61//! // Create an empty "scope arena", which we need for allocating scope guards.
62//! let mut scope_arena = ScopeArena::new();
63//! // Create an empty elastic which expects to be loaned an `&'a mut u32`.
64//! let empty_elastic = ElasticMut::<u32>::new();
65//!
66//! {
67//!     // Elastics are shared, using an Arc under the hood. Cloning them is cheap
68//!     // and does not clone any data inside.
69//!     let shared_elastic = empty_elastic.clone();
70//!     let mut refcell_guard = my_special_u32.borrow_mut();
71//!     scope_arena.scope(|guard| {
72//!         // If you can get an `&'a mut T` out of it, you can loan it to an `ElasticMut<T>`.
73//!         guard.loan(&shared_elastic, &mut *refcell_guard);
74//!
75//!         // Spawn a thread to do some computation with our borrowed u32, and take ownership of
76//!         // the clone we made of our Elastic.
77//!         std::thread::spawn(move || {
78//!             // Internally, `Elastic` contains an atomic refcell. This is necessary for safety,
79//!             // so unfortunately we have to suck it up and take the extra verbosity.
80//!             *shared_elastic.borrow_mut() *= 3;
81//!         }).join().unwrap();
82//!
83//!         // At the end of this scope, the borrowed reference is forcibly removed from the
84//!         // shared Elastic, and the lifetime bounds on `scope` ensure that the refcell guard is
85//!         // still valid at this point. However, the value inside the refcell has long since been
86//!         // modified by our spawned thread!
87//!     });
88//!
89//!     // Now, the refcell guard drops.
90//! }
91//!
92//! // The elastic never took ownership of the refcell or the value inside in any way - it was
93//! // temporarily loaned an `&'a mut u32` which came from inside a `core::cell::RefMut<'a, u32>`
94//! // and did any modifications directly on that reference. So code "after" any elastic
95//! // manipulation looks exactly the same as before - no awkward wrappers or anything.
96//! let important_computed_value = *my_special_u32.borrow() * 6 + 6;
97//!
98//! // With the current design of Elastic, the scope arena will not automagically release the memory
99//! // it allocated, so if it's used in a loop, you'll probably want to call `.reset()` occasionally
100//! // to release the memory used to allocate the scope guards:
101//! scope_arena.reset();
102//! ```
103//!
104//! ## Using `Elastic` for enabling dynamic typing with non-`'static` values
105//!
106//! Rust's [`Any`](core::any::Any) trait is an invaluable tool for writing code which doesn't always
107//! have static knowledge of the types involved. However, when it comes to lifetimes, the question
108//! of how to handle dynamic typing is complex and unresolved. Should a `Foo<'a>` have a different
109//! type ID from a `Foo<'static>`? As the thread discussing this is the greatest thread in the
110//! history of forums, locked by a moderator after 12,239 pages of heated debate, (it was not and I
111//! am not aware of any such thread; this is a joke) [`Any`](core::any::Any) has a `'static` bound
112//! on it. Which is very inconvenient if what you want to do is completely *ignore* any lifetimes in
113//! your dynamically typed code by treating them as if they're all equal (and/or `'static`.)
114//!
115//! Elastic can help here. If the type you want to stick into an `Any` is an `&T` or `&mut T`, it's
116//! very straightforward as [`ElasticRef<T>`] and [`ElasticMut<T>`] are both `'static`. If the type
117//! you have is not a plain old reference, it's a bit nastier; you need to ensure the safety of
118//! lifetime manipulation on the type in question, and then manually construct a type which
119//! represents (as plain old data) a lifetime-erased version of that type. Then, manually implement
120//! [`Stretched`] and [`Stretchable`] on it. This is ***highly*** unsafe! Please take special care
121//! to respect the requirements on implementations of `Stretchable`. Size and alignment of the
122//! stretched type must match. This is pretty much the only requirement though. The
123//! [`impl_stretched_methods`] macro exists to help you safely implement the methods required by
124//! `Stretched` once you ensure the lifetimes are correct. Just note that it does this by swinging
125//! a giant hammer named [`core::mem::transmute`], and it does this *by design*, and that **if you
126//! screw up on the lifetime safety requirements you are headed on a one way trip to
127//! Undefinedbehaviortown.**
128//!
129//! ## Yes, there are twelve different ways to borrow from an `Elastic`, and every single one is useful
130//!
131//! How may I borrow from thee? Well, let me count the ways:
132//!
133//! ### Dereferenced borrows (the kind you want most of the time)
134//!
135//! Eight of the borrow methods are "dereferenced" - they expect you to be stretching references or
136//! smart pointers/guards with lifetimes in them. If you're using [`ElasticRef`]/[`ElasticMut`] or
137//! [`StretchedRef`]/[`StretchedMut`], these are what you want; they're much more convenient than
138//! the parameterized borrows.
139//!
140//! - [`Elastic::borrow`] and [`Elastic::borrow_mut`]; most of the time, if you're working with
141//!   references being extended, and you don't care about handling borrow errors, you'll use these.
142//!   99% of the time, they do what you want, and you're probably going to be enforcing invariants
143//!   to make sure it wouldn't error anyways. These are the [`Elastic`] versions of
144//!   [`RefCell::borrow`] and [`RefCell::borrow_mut`], and they pretty much behave identicaly.
145//! - [`Elastic::borrow_arc`] and [`Elastic::borrow_arc_mut`]; these come from the [`ArcCell`] which
146//!   lives inside an [`Elastic`], and offer guards juts like `borrow` and `borrow_mut` *but*, those
147//!   guards are reference counted and don't have a lifetime attached. So instead of
148//!   [`AtomicRef<'a, T>`], you get [`ArcRef<T>`], which has the same lifetime as `T`... and if `T`
149//!   is `'static`, so is [`ArcRef<T>`]/[`ArcRefMut<T>`], which can be very useful, again for
150//!   passing across `Send`/`'static`/whatnot boundaries.
151//! - [`Elastic::try_borrow`], [`Elastic::try_borrow_mut`], [`Elastic::try_borrow_arc`],
152//!   [`Elastic::try_borrow_arc_mut`]; these are just versions of the four
153//!   `borrow`/`borrow_mut`/`borrow_arc`/`borrow_arc_mut` methods which don't panic on failure, and
154//!   return `Result` instead.
155//!
156//! ### Parameterized borrows (you're in the deep end, now)
157//!
158//! The last four methods are what the other eight are all implemented on. These return `Result`
159//! instead of panicking, and provide direct access to whatever [`T::Parameterized<'a>`] is. In the
160//! case of [`StretchedRef`] and [`StretchedMut`], we have `<StretchedRef<T>>::Parameterized<'a> =
161//! &'a T` and `<StretchedMut<T>>::Parameterized<'a> = &'a mut T`; when we use a method like
162//! [`Elastic::try_borrow_as_parameterized_mut`] on an [`Elastic<StretchedMut<T>>`], we'll get back
163//! `Result<AtomicRefMut<'_, &'_ mut T>, BorrowMutError>` which is pretty obviously redundant. It's
164//! for this reason that the other eight methods exist to handle the common cases and abstract away
165//! the fact that [`Elastic`] is more than just an [`AtomicRefCell`].
166//!
167//! # Safety: [`ElasticGuard`], [`ScopeGuard`] and [`ScopeArena`]
168//!
169//! [`Elastic`] works by erasing the lifetime on the type and then leaving you with an
170//! [`ElasticGuard<'a>`] which preserves the lifetime. This [`ElasticGuard`] is a "drop guard" - in
171//! its [`Drop`] implementation, it tries to take back the loaned value, preventing it from being
172//! used after the loan expires. There are a couple ways this can go wrong:
173//!
174//! 1. If [`Elastic`] is currently borrowed when the guard drops, a panic will occur, because the
175//!    guard's drop impl needs mutable access to the "slot" inside the [`Elastic`].
176//! 2. If you [`core::mem::forget`] the [`ElasticGuard`], the slot inside the [`Elastic`] will never
177//!    be cleared, which is *highly* unsafe, as you now have a stretched value running around which
178//!    is no longer bounded by any lifetime. This is a recipe for undefined behavior and
179//!    use-after-free bugs.
180//!
181//! The first error case is unavoidable; if you're loaning stuff out, you might have to drop
182//! something while it's in use. To avoid this, loaning should be done in phases; loan a bunch of
183//! things at once, ensure whatever is using those loans finishes, and then expire those loans.
184//! Thankfully, the solution to making this easy *and* avoiding the possibility of the second
185//! failure mode can exist in one primitive: [`ScopeArena`]. [`ScopeArena`] provides a method
186//! [`ScopeArena::scope`], which allows you to create scopes in which a [`ScopeGuard`] takes
187//! ownership of the [`ElasticGuard`]s produced by the loaning operation. Since the [`ScopeGuard`]
188//! is owned by the caller - [`ScopeArena::scope`] - the user cannot accidentally or intentionally
189//! [`core::mem::forget`] a guard, and in addition, the guard ensures that all of the loans made to
190//! it have the same parameterized lifetime, which encourages the phase-loaning pattern.
191//!
192//! In short, *always use [`ScopeArena`] and [`ScopeGuard`] - if you think you have to use
193//! [`ElasticGuard`] for some reason, double check!*
194//!
195//! [`Stretchable<'a>`]: crate::Stretchable
196//! [`Arc`]: alloc::sync::Arc
197//! [`RefCell<T>`]: core::cell::RefCell
198//! [`AtomicRefCell`]: hv_cell::AtomicRefCell
199//! [`AtomicRef<'a, T>`]: hv_cell::AtomicRef
200//! [`ElasticGuard<'a, T>`]: crate::ElasticGuard
201//! [`RefCell::borrow`]: core::cell::RefCell::borrow
202//! [`RefCell::borrow_mut`]: core::cell::RefCell::borrow_mut
203
204#![cfg_attr(not(feature = "std"), no_std)]
205#![warn(missing_docs)]
206#![warn(missing_debug_implementations)]
207#![feature(generic_associated_types, allocator_api)]
208
209extern crate alloc;
210
211use core::{
212    fmt,
213    marker::PhantomData,
214    ops::{Deref, DerefMut},
215    ptr::NonNull,
216};
217
218// Used by `impl_stretched_methods`.
219#[doc(hidden)]
220pub use core::mem::transmute;
221
222use alloc::vec::Vec;
223use hv_guarded_borrow::{
224    NonBlockingGuardedBorrow, NonBlockingGuardedBorrowMut, NonBlockingGuardedMutBorrowMut,
225};
226
227use hv_cell::{ArcCell, ArcRef, ArcRefMut, AtomicRef, AtomicRefMut};
228use hv_stampede::{boxed::Box as ArenaBox, Bump};
229
230/// Small convenience macro for correctly implementing the four unsafe methods of [`Stretched`].
231#[macro_export]
232macro_rules! impl_stretched_methods {
233    () => {
234        unsafe fn lengthen(this: Self::Parameterized<'_>) -> Self {
235            $crate::transmute(this)
236        }
237
238        unsafe fn shorten<'a>(this: Self) -> Self::Parameterized<'a> {
239            $crate::transmute(this)
240        }
241
242        unsafe fn shorten_mut<'a>(this: &'_ mut Self) -> &'_ mut Self::Parameterized<'a> {
243            $crate::transmute(this)
244        }
245
246        unsafe fn shorten_ref<'a>(this: &'_ Self) -> &'_ Self::Parameterized<'a> {
247            $crate::transmute(this)
248        }
249    };
250}
251
252#[cfg(any(feature = "hv-ecs"))]
253pub mod external;
254
255/// Marker trait indicating that a type can be stretched (has a type for which there is an
256/// implementation of `Stretched`, and which properly "translates back" with its `Parameterized`
257/// associated type.)
258pub trait Stretchable<'a>: 'a {
259    /// The type you get by stretching `Self`.
260    #[rustfmt::skip]
261    type Stretched: Stretched<Parameterized<'a> = Self>;
262}
263
264/// A type which is a "stretched" version of a second type, representing the result of having every
265/// single lifetime in that type set to `'static`.
266///
267/// # Safety
268///
269/// Holy shit this is incredibly fucking unsafe. It's cursed. It's so unbelievably cursed I'd like
270/// to forget I wrote it but in all the circumstances I want to use it it should be safe so fine,
271/// whatever. For the sake of completeness, however, I will say that there are *TWO major
272/// requirements:*
273///
274/// ## Requirement #1: Do not use `'static` or references to represent your stretched type!
275///
276/// ***DEPENDING ON YOUR TYPE, IT MAY BE UNDEFINED BEHAVIOR TO ACTUALLY HAVE IT REPRESENTED WITH THE
277/// PARAMETERIZED LIFETIME SUBSTITUTED WITH `'static`!*** The Rust aliasing rules state that if you
278/// turn a pointer to some `T` - which includes a reference to a `T` - into a reference with a given
279/// lifetime... then ***you must respect the aliasing rules with respect to that lifetime for the
280/// rest of the lifetime, even if you get rid of the value and it is no longer touched!*** Instead
281/// of using `'static` lifetimes to represent a stretched thing, use pointers, or - horror of
282/// horrors - implement `Stretched` for something like:
283///
284/// ```
285/// # #![feature(generic_associated_types)]
286/// # use hv_elastic::{Stretched, Stretchable};
287/// # pub struct MyType<'a>(&'a ());
288///
289/// pub struct StretchedMyType {
290///     // An array of bytes gives us the exact size of the type we're stretching.
291///     _data: [u8; std::mem::size_of::<MyType>()],
292///     // And, a zero-sized array of a `'static` version of the type we're stretching gives us
293///     // the required alignment. Because it's a zero-sized array, no values of the type
294///     // `MyType<'static>` actually end up existing, so it's safe to use `'static` here.
295///     _force_align: [MyType<'static>; 0],
296/// }
297///
298/// // It is recommended to use `static_assertions` and always follow a definition like this with
299/// // assertions that the alignment and size match, as required by the `Stretched` trait.
300/// static_assertions::assert_eq_align!(MyType<'static>, StretchedMyType);
301/// static_assertions::assert_eq_size!(MyType<'static>, StretchedMyType);
302///
303/// unsafe impl Stretched for StretchedMyType {
304///     type Parameterized<'a> = MyType<'a>;
305///
306///     hv_elastic::impl_stretched_methods!();
307/// }
308///
309/// impl<'a> Stretchable<'a> for MyType<'a> {
310///     type Stretched = StretchedMyType;
311/// }
312/// ```
313///
314/// This creates a piece of plain old data with the same size and byte alignment as your type. Yes,
315/// this will work. And yes, it is a much safer option/will not cause undefined behavior, unlike
316/// having `&'static MyType` around and having Rust assume that the thing it pointed to will never,
317/// ever be mutated again. **DO NOT NEEDLESSLY ANTAGONIZE THE RUST COMPILER! THE CRAB WILL NOT
318/// FORGIVE YOU!**
319///
320/// ## Requirement #2: Your stretchable type must be covariant over the parameterized lifetime!
321///
322/// A type which is stretchable is parameterized over a lifetime. *It **must** be covariant over
323/// that lifetime.* The reason for this is that essentially the `Stretched` trait and [`Elastic`]
324/// allow you to *decouple two lifetimes at a number of "decoupled reborrows".* The first lifetime
325/// here is the lifetime of the original data, which is carried over in [`ElasticGuard`];
326/// [`ElasticGuard`] ensures that the data is dropped at or before the end of its lifetime (and if
327/// it can't, everything will blow up with a panic.) The second lifetime is the lifetime of every
328/// borrow from the [`Elastic`]. As such what [`Elastic`] and [`Stretchable`] actually allow you to
329/// do is tell Rust to *assume* that the reborrowed lifetimes are all outlived by the original
330/// lifetime, and blow up/error if not. This should scare you shitless. However, I am unstoppable
331/// and I won't do what you tell me.
332///
333/// That is all. Godspeed.
334pub unsafe trait Stretched: 'static + Sized {
335    /// The parameterized type, which must be bit-equivalent to the unparameterized `Self` type. It
336    /// must have the same size, same pointer size, same alignment, same *everything.*
337    type Parameterized<'a>: Stretchable<'a, Stretched = Self>
338    where
339        Self: 'a;
340
341    /// Lengthen the lifetime of a [`Stretched::Parameterized`] to `'static`.
342    ///
343    /// # Safety
344    ///
345    /// This is highly unsafe, and care must be taken to ensure that the lengthened data is taken
346    /// care of and not discarded before the actual lifetime of the data. This function should be
347    /// implemented as a wrapper around [`core::mem::transmute`]; this should give you a hint as to
348    /// just how wildly unsafe this can be if mishandled.
349    unsafe fn lengthen(this: Self::Parameterized<'_>) -> Self;
350
351    /// Shorten the lifetime of a `'static` self to some arbitrary [`Stretched::Parameterized`].
352    /// This is intended strictly as the inverse of [`Stretched::lengthen`], and makes no guarantees
353    /// about its behavior if not used as such.
354    ///
355    /// # Safety
356    ///
357    /// Shortening a lifetime is normally totally safe, but this function might be usable in cases
358    /// where the lifetime is actually invariant. In this case, it is extremely unsafe and care must
359    /// be taken to ensure that the lifetime of the shortened data is the same as the lifetime of
360    /// the data before its lifetime was lengthened. This function should be simply implemented as a
361    /// wrapper around [`core::mem::transmute`]; this should give you a hint as to just how wildly
362    /// unsafe this can be if mishandled.
363    unsafe fn shorten<'a>(this: Self) -> Self::Parameterized<'a>;
364
365    /// Equivalent to [`Stretched::shorten`] but operates on a mutable reference to the stretched
366    /// type.
367    ///
368    /// # Safety
369    ///
370    /// Same as [`Stretched::shorten`]. Should be implemented simply as a wrapper around transmute.
371    unsafe fn shorten_mut<'a>(this: &'_ mut Self) -> &'_ mut Self::Parameterized<'a>;
372
373    /// Equivalent to [`Stretched::shorten`] but operates on an immutable reference to the stretched
374    /// type.
375    ///
376    /// # Safety
377    ///
378    /// Same as [`Stretched::shorten`]. Should be implemented simply as a wrapper around transmute.
379    unsafe fn shorten_ref<'a>(this: &'_ Self) -> &'_ Self::Parameterized<'a>;
380}
381
382/// A guard representing a loan of some stretchable value to some [`Elastic`]. On drop, it expires
383/// the loan, borrowing the elastic's inner slot mutably (panicking if not possible) and
384///
385/// Leaking a value of this type can cause undefined behavior. You normally do not want to
386/// manipulate these directly. Prefer [`ScopeArena`] and [`ScopeGuard`] whenever possible.
387pub struct ElasticGuard<'a, T: Stretchable<'a>> {
388    slot: ArcCell<Option<T::Stretched>>,
389    _phantom: PhantomData<fn(&'a ())>,
390}
391
392impl<'a, T: Stretchable<'a>> fmt::Debug for ElasticGuard<'a, T> {
393    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
394        write!(f, "ElasticGuard {{ .. }}")
395    }
396}
397
398impl<'a, T: Stretchable<'a>> ElasticGuard<'a, T> {
399    /// Revoke the loan and retrieve the loaned value. Panicks if the value is currently borrowed
400    /// mutably.
401    pub fn take(self) -> T {
402        let stretched = self
403            .slot
404            .as_inner()
405            .borrow_mut()
406            .take()
407            .expect("empty slot!");
408        let shortened = unsafe { <T::Stretched>::shorten(stretched) };
409        core::mem::forget(self);
410        shortened
411    }
412}
413
414impl<'a, T: Stretchable<'a>> Drop for ElasticGuard<'a, T> {
415    fn drop(&mut self) {
416        if let Some(stretched) = self.slot.as_inner().borrow_mut().take() {
417            drop(unsafe { <T::Stretched>::shorten(stretched) });
418        }
419    }
420}
421
422/// The error returned when an immutable borrow fails.
423#[derive(Debug, Clone, Copy)]
424#[cfg_attr(feature = "std", derive(thiserror::Error))]
425pub enum BorrowError {
426    /// Couldn't borrow because the elastic was already mutably borrowed somewhere.
427    #[cfg_attr(feature = "std", error("the elastic is already mutably borrowed"))]
428    MutablyBorrowed,
429    /// Couldn't borrow because the elastic either hadn't been loaned to, or any outstanding loaned
430    /// values were already repossessed by destroying their [`ElasticGuard`].
431    #[cfg_attr(feature = "std", error("the elastic is empty; it has not been loaned to, or any outstanding loan has been repossessed"))]
432    EmptySlot,
433}
434
435/// The error returned when a mutable borrow fails.
436#[derive(Debug, Clone, Copy)]
437#[cfg_attr(feature = "std", derive(thiserror::Error))]
438pub enum BorrowMutError {
439    /// Couldn't borrow because the elastic was already borrowed somewhere.
440    #[cfg_attr(feature = "std", error("the elastic is already borrowed"))]
441    Borrowed,
442    /// Couldn't borrow because the elastic either hadn't been loaned to, or any outstanding loaned
443    /// values were already repossessed by destroying their [`ElasticGuard`].
444    #[cfg_attr(feature = "std", error("the elastic is empty; it has not been loaned to, or any outstanding loan has been repossessed"))]
445    EmptySlot,
446}
447
448/// A container for a stretched value.
449///
450/// This acts a bit like `Arc<AtomicRefCell<Option<T>>>`, through its `Clone` behavior and borrowing
451/// methods, but the similarity ends there. The only way to put data into this type is through
452/// [`Elastic::loan`], which allows you to safely loan some [`Stretchable`], non-`'static` type, to
453/// an [`Elastic`] carrying the corresponding [`Stretched`] type. As [`Stretched`] requires
454/// `'static`, [`Elastic<T>`] is always `'static` and can be used to access non-`'static` types
455/// safely from contexts which require `'static` (such as dynamic typing with `Any` or the
456/// `hv-alchemy` crate.) The lifetime is preserved by a scope guard, [`ElasticGuard`], which is
457/// provided when a value is loaned and which revokes the loan when it is dropped or has the loaned
458/// value forcibly taken back by [`ElasticGuard::take`]. In most cases, you won't actually see these
459/// [`ElasticGuard`]s as they'll be created and stashed away by a [`ScopeGuard`], which you should
460/// be using to perform all your loans.
461///
462/// [`Elastic<T>`] is thread-safe. However, internally, it uses an
463/// [`AtomicRefCell`](hv_cell::AtomicRefCell), so if you violate borrowing invariants, you will have
464/// a panic on your hands. This goes likewise for taking the value back w/ [`ElasticGuard`] or
465/// dropping the guard: the guard will panic if it cannot take back the value.
466///
467/// # Soundness
468///
469/// As long as you fulfill its requirements with respect to its guards, `Elastic` is sound in all
470/// normal modes of operation. However, in the case of a panic across threads, bad things can
471/// happen:
472///
473/// - If an elastic exists on one thread and is being loaned to with a guard on another thread, and
474///   the thread holding the guard panicks, it is unclear what will/should happen to the other
475///   thread. Most likely, the thread holding the guard will attempt to drop the guard, which will
476///   then cause the guard's `Drop` impl to panic, which will then cause an abort (as panicking
477///   during a panick will cause an abort.) However, if the panic *starts* in the guard's `Drop`
478///   impl, and the guard is handling a reference which has been loaned to it, it is possible that
479///   the original owner of the data being borrowed there could be dropped, *while* the other thread
480///   is holding an open borrow on a reference to that data. Since this lib is `no_std`, we cannot
481///   `std::process::abort()`. We may be able to consider this soundness hole plugged by marking
482///   this type `!UnwindSafe` or requiring that stretched types be `UnwindSafe`; this is an open
483///   question.
484///
485/// So in short, unless you're worried about recovering from panicks across threads where the thread
486/// panicking is the one that originally owned the data (and most likely the parent thread),
487/// `Elastic` is thought to be completely sound.
488pub struct Elastic<T: Stretched> {
489    slot: ArcCell<Option<T>>,
490}
491
492impl<T: Stretched> fmt::Debug for Elastic<T> {
493    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
494        write!(f, "Elastic {{ ... }}")
495    }
496}
497
498impl<T: Stretched> Clone for Elastic<T> {
499    fn clone(&self) -> Self {
500        Self {
501            slot: self.slot.clone(),
502        }
503    }
504}
505
506impl<T: Stretched> Default for Elastic<T> {
507    fn default() -> Self {
508        Self::new()
509    }
510}
511
512impl<T: Stretched> Elastic<T> {
513    /// Create an empty [`Elastic<T>`].
514    pub fn new() -> Self {
515        Self {
516            slot: Default::default(),
517        }
518    }
519
520    /// Immutably borrow the loaned value. Panicks if the elastic is already mutably borrowed or if
521    /// it was never loaned to/any loans have expired.
522    ///
523    /// This method assumes that the stretchable type is a reference or smart pointer and can be
524    /// immediately dereferenced; if that's not the case, please use `try_borrow_as_parameterized`.
525    #[track_caller]
526    pub fn borrow<'a>(&'a self) -> AtomicRef<'a, <T::Parameterized<'a> as Deref>::Target>
527    where
528        T::Parameterized<'a>: Deref,
529    {
530        self.try_borrow().unwrap()
531    }
532
533    /// Mutably borrow the loaned value. Panicks if the elastic is already borrowed or if it was
534    /// never loaned to/any loans have expired.
535    ///
536    /// This method assumes that the stretchable type is a reference or smart pointer and can be
537    /// immediately dereferenced; if that's not the case, please use
538    /// `try_borrow_as_parameterized_mut`.
539    #[track_caller]
540    pub fn borrow_mut<'a>(&'a self) -> AtomicRefMut<'a, <T::Parameterized<'a> as Deref>::Target>
541    where
542        T::Parameterized<'a>: DerefMut,
543    {
544        self.try_borrow_mut().unwrap()
545    }
546
547    /// Immutably borrow the loaned value through a reference-counted guard. Panicks if the
548    /// elastic is already mutably borrowed or if it was never loaned to/any loans have expired.
549    ///
550    /// This method assumes that the stretchable type is a reference or smart pointer and can be
551    /// immediately dereferenced; if that's not the case, please use `try_borrow_as_parameterized_arc`.
552    #[track_caller]
553    pub fn borrow_arc<'a>(&'a self) -> ArcRef<<T::Parameterized<'a> as Deref>::Target, Option<T>>
554    where
555        T::Parameterized<'a>: Deref,
556    {
557        self.try_borrow_arc().unwrap()
558    }
559
560    /// Mutably borrow the loaned value through a reference-counted guard. Panicks if the
561    /// elastic is already borrowed or if it was never loaned to/any loans have expired.
562    ///
563    /// This method assumes that the stretchable type is a reference or smart pointer and can be
564    /// immediately dereferenced; if that's not the case, please use
565    /// `try_borrow_as_parameterized_arc_mut`.
566    #[track_caller]
567    pub fn borrow_arc_mut<'a>(
568        &'a self,
569    ) -> ArcRefMut<<T::Parameterized<'a> as Deref>::Target, Option<T>>
570    where
571        T::Parameterized<'a>: DerefMut,
572    {
573        self.try_borrow_arc_mut().unwrap()
574    }
575
576    /// Immutably borrow the loaned value. Returns `Err` if the elastic is already mutably borrowed
577    /// or if it was never loaned to/any loans have expired.
578    ///
579    /// This method assumes that the stretchable type is a reference or smart pointer and can be
580    /// immediately dereferenced; if that's not the case, please use `try_borrow_as_parameterized`.
581    #[track_caller]
582    pub fn try_borrow<'a>(
583        &'a self,
584    ) -> Result<AtomicRef<'a, <T::Parameterized<'a> as Deref>::Target>, BorrowError>
585    where
586        T::Parameterized<'a>: Deref,
587    {
588        let guard = self.try_borrow_as_parameterized()?;
589        Ok(AtomicRef::map(guard, |t| &**t))
590    }
591
592    /// Immutably borrow the loaned value through a reference-counted guard. Returns `Err` if the
593    /// elastic is already mutably borrowed or if it was never loaned to/any loans have expired.
594    ///
595    /// This method assumes that the stretchable type is a reference or smart pointer and can be
596    /// immediately dereferenced; if that's not the case, please use `try_borrow_as_parameterized_arc`.
597    #[track_caller]
598    pub fn try_borrow_arc<'a>(
599        &'a self,
600    ) -> Result<ArcRef<<T::Parameterized<'a> as Deref>::Target, Option<T>>, BorrowError>
601    where
602        T::Parameterized<'a>: Deref,
603    {
604        let arc_ref = self.try_borrow_as_parameterized_arc()?;
605        Ok(ArcRef::map(arc_ref, |t| &**t))
606    }
607
608    /// Mutably borrow the loaned value through a reference-counted guard. Returns `Err` if the
609    /// elastic is already borrowed or if it was never loaned to/any loans have expired.
610    ///
611    /// This method assumes that the stretchable type is a reference or smart pointer and can be
612    /// immediately dereferenced; if that's not the case, please use
613    /// `try_borrow_as_parameterized_arc_mut`.
614    #[track_caller]
615    pub fn try_borrow_arc_mut<'a>(
616        &'a self,
617    ) -> Result<ArcRefMut<<T::Parameterized<'a> as Deref>::Target, Option<T>>, BorrowMutError>
618    where
619        T::Parameterized<'a>: DerefMut,
620    {
621        let arc_mut = self.try_borrow_as_parameterized_arc_mut()?;
622        Ok(ArcRefMut::map(arc_mut, |t| &mut **t))
623    }
624
625    /// Mutably borrow the loaned value. Returns `Err` if the elastic is already borrowed or if it
626    /// was never loaned to/any loans have expired.
627    ///
628    /// This method assumes that the stretchable type is a refence or smart pointer and can be
629    /// immediately dereferenced; if that's not the case, please use
630    /// `try_borrow_as_parameterized_mut`.
631    #[track_caller]
632    pub fn try_borrow_mut<'a>(
633        &'a self,
634    ) -> Result<AtomicRefMut<'a, <T::Parameterized<'a> as Deref>::Target>, BorrowMutError>
635    where
636        T::Parameterized<'a>: DerefMut,
637    {
638        let guard = self.try_borrow_as_parameterized_mut()?;
639        Ok(AtomicRefMut::map(guard, |t| &mut **t))
640    }
641
642    /// Attempt to immutably borrow the loaned value, if present.
643    #[track_caller]
644    pub fn try_borrow_as_parameterized(
645        &self,
646    ) -> Result<AtomicRef<T::Parameterized<'_>>, BorrowError> {
647        let guard = self
648            .slot
649            .as_inner()
650            .try_borrow()
651            .map_err(|_| BorrowError::MutablyBorrowed)?;
652        AtomicRef::filter_map(guard, Option::as_ref)
653            .map(|arm| AtomicRef::map(arm, |t| unsafe { T::shorten_ref(t) }))
654            .ok_or(BorrowError::EmptySlot)
655    }
656
657    /// Attempt to mutably borrow the loaned value, if present.
658    #[track_caller]
659    pub fn try_borrow_as_parameterized_mut(
660        &self,
661    ) -> Result<AtomicRefMut<T::Parameterized<'_>>, BorrowMutError> {
662        let guard = self
663            .slot
664            .as_inner()
665            .try_borrow_mut()
666            .map_err(|_| BorrowMutError::Borrowed)?;
667        AtomicRefMut::filter_map(guard, Option::as_mut)
668            .map(|arm| AtomicRefMut::map(arm, |t| unsafe { T::shorten_mut(t) }))
669            .ok_or(BorrowMutError::EmptySlot)
670    }
671
672    /// Attempt to immutably borrow the loaned value, via a reference-counted guard.
673    #[track_caller]
674    pub fn try_borrow_as_parameterized_arc(
675        &self,
676    ) -> Result<ArcRef<T::Parameterized<'_>, Option<T>>, BorrowError> {
677        let guard = self
678            .slot
679            .try_borrow()
680            .map_err(|_| BorrowError::MutablyBorrowed)?;
681        ArcRef::filter_map(guard, Option::as_ref)
682            .map(|arc| ArcRef::map(arc, |t| unsafe { T::shorten_ref(t) }))
683            .ok_or(BorrowError::EmptySlot)
684    }
685
686    /// Attempt to mutably borrow the loaned value, via a reference-counted guard..
687    #[track_caller]
688    pub fn try_borrow_as_parameterized_arc_mut(
689        &self,
690    ) -> Result<ArcRefMut<T::Parameterized<'_>, Option<T>>, BorrowMutError> {
691        let guard = self
692            .slot
693            .try_borrow_mut()
694            .map_err(|_| BorrowMutError::Borrowed)?;
695        ArcRefMut::filter_map(guard, Option::as_mut)
696            .map(|arc| ArcRefMut::map(arc, |t| unsafe { T::shorten_mut(t) }))
697            .ok_or(BorrowMutError::EmptySlot)
698    }
699
700    /// Loan a stretchable value to this [`Elastic`] in exchange for a guard object which ends the
701    /// loan when the value is taken back or when the guard is dropped.
702    ///
703    /// Panics if there is already a loan in progress to this [`Elastic`].
704    ///
705    /// # Safety
706    ///
707    /// The guard *must* have its destructor run by the end of its lifetime, either by dropping it
708    /// or using [`ElasticGuard::take`]. Calling [`core::mem::forget`] on an [`ElasticGuard`] is
709    /// considered instant undefined behavior, as it leaves an [`Elastic`] in a state which is not
710    /// well-defined and potentially contains a stretched value which is long past the end of its
711    /// life, causing a use-after-free.
712    #[track_caller]
713    pub unsafe fn loan<'a>(
714        &self,
715        t: T::Parameterized<'a>,
716    ) -> ElasticGuard<'a, T::Parameterized<'a>> {
717        let mut slot = self.slot.as_inner().borrow_mut();
718        assert!(
719            slot.is_none(),
720            "Elastic is already in the middle of a loan!"
721        );
722        let stretched = T::lengthen(t);
723        *slot = Some(stretched);
724
725        ElasticGuard {
726            slot: self.slot.clone(),
727            _phantom: PhantomData,
728        }
729    }
730}
731
732impl<T: Stretched, U: ?Sized> NonBlockingGuardedBorrow<U> for Elastic<T>
733where
734    for<'a> T::Parameterized<'a>: core::borrow::Borrow<U>,
735{
736    type Guard<'a>
737    where
738        U: 'a,
739    = AtomicRef<'a, U>;
740    type BorrowError<'a>
741    where
742        U: 'a,
743    = BorrowError;
744
745    fn try_nonblocking_guarded_borrow(&self) -> Result<Self::Guard<'_>, Self::BorrowError<'_>> {
746        self.try_borrow_as_parameterized()
747            .map(|guard| AtomicRef::map(guard, |t| core::borrow::Borrow::borrow(t)))
748    }
749}
750
751impl<T: Stretched, U: ?Sized> NonBlockingGuardedBorrowMut<U> for Elastic<T>
752where
753    for<'a> T::Parameterized<'a>: core::borrow::BorrowMut<U>,
754{
755    type GuardMut<'a>
756    where
757        U: 'a,
758    = AtomicRefMut<'a, U>;
759    type BorrowMutError<'a>
760    where
761        U: 'a,
762    = BorrowMutError;
763
764    fn try_nonblocking_guarded_borrow_mut(
765        &self,
766    ) -> Result<Self::GuardMut<'_>, Self::BorrowMutError<'_>> {
767        self.try_borrow_as_parameterized_mut()
768            .map(|guard| AtomicRefMut::map(guard, |t| core::borrow::BorrowMut::borrow_mut(t)))
769    }
770}
771
772impl<T: Stretched, U: ?Sized> NonBlockingGuardedMutBorrowMut<U> for Elastic<T>
773where
774    for<'a> T::Parameterized<'a>: core::borrow::BorrowMut<U>,
775{
776    type MutGuardMut<'a>
777    where
778        U: 'a,
779    = AtomicRefMut<'a, U>;
780    type MutBorrowMutError<'a>
781    where
782        U: 'a,
783    = BorrowMutError;
784
785    fn try_nonblocking_guarded_mut_borrow_mut(
786        &mut self,
787    ) -> Result<Self::MutGuardMut<'_>, Self::MutBorrowMutError<'_>> {
788        self.try_borrow_as_parameterized_mut()
789            .map(|guard| AtomicRefMut::map(guard, |t| core::borrow::BorrowMut::borrow_mut(t)))
790    }
791}
792
793/// A type representing a stretched `&T` reference. Has the same representation as a `*const T`.
794#[derive(Debug, Clone, Copy)]
795#[repr(transparent)]
796pub struct StretchedRef<T: ?Sized>(*const T);
797
798unsafe impl<T: Sync> Send for StretchedRef<T> {}
799unsafe impl<T: Sync> Sync for StretchedRef<T> {}
800
801/// A type representing a stretched `&mut T` reference. Has the same representation as a `*mut T`.
802#[derive(Debug, Clone, Copy)]
803#[repr(transparent)]
804pub struct StretchedMut<T: ?Sized>(NonNull<T>);
805
806unsafe impl<T: Send> Send for StretchedMut<T> {}
807unsafe impl<T: Sync> Sync for StretchedMut<T> {}
808
809unsafe impl<T: 'static> Stretched for StretchedRef<T> {
810    type Parameterized<'a> = &'a T;
811
812    unsafe fn lengthen(this: Self::Parameterized<'_>) -> Self {
813        core::mem::transmute(this)
814    }
815
816    unsafe fn shorten<'a>(this: Self) -> Self::Parameterized<'a> {
817        &*this.0.cast()
818    }
819
820    unsafe fn shorten_mut<'a>(this: &'_ mut Self) -> &'_ mut Self::Parameterized<'a> {
821        core::mem::transmute(this)
822    }
823
824    unsafe fn shorten_ref<'a>(this: &'_ Self) -> &'_ Self::Parameterized<'a> {
825        core::mem::transmute(this)
826    }
827}
828
829impl<'a, T: 'static> Stretchable<'a> for &'a T {
830    type Stretched = StretchedRef<T>;
831}
832
833unsafe impl<T: 'static> Stretched for StretchedMut<T> {
834    type Parameterized<'a> = &'a mut T;
835
836    unsafe fn lengthen(this: Self::Parameterized<'_>) -> Self {
837        core::mem::transmute(this)
838    }
839
840    unsafe fn shorten<'a>(this: Self) -> Self::Parameterized<'a> {
841        this.0.cast().as_mut()
842    }
843
844    unsafe fn shorten_mut<'a>(this: &'_ mut Self) -> &'_ mut Self::Parameterized<'a> {
845        core::mem::transmute(this)
846    }
847
848    unsafe fn shorten_ref<'a>(this: &'_ Self) -> &'_ Self::Parameterized<'a> {
849        core::mem::transmute(this)
850    }
851}
852
853impl<'a, T: 'static> Stretchable<'a> for &'a mut T {
854    type Stretched = StretchedMut<T>;
855}
856
857macro_rules! impl_tuple {
858    ($($letter:ident),*) => {
859        unsafe impl<$($letter: Stretched,)*> Stretched for ($($letter,)*) {
860            type Parameterized<'a> = ($(<$letter as Stretched>::Parameterized<'a>,)*);
861
862            #[allow(non_snake_case, clippy::unused_unit)]
863            unsafe fn lengthen(this: ($(<$letter as Stretched>::Parameterized<'_>,)*)) -> Self {
864                let ($($letter,)*) = this;
865                ($($letter::lengthen($letter),)*)
866            }
867
868            #[allow(non_snake_case, clippy::unused_unit)]
869            unsafe fn shorten<'a>(this: Self) -> Self::Parameterized<'a> {
870                let ($($letter,)*) = this;
871                ($($letter::shorten($letter),)*)
872            }
873
874            unsafe fn shorten_mut<'a>(this: &'_ mut Self) -> &'_ mut Self::Parameterized<'a> {
875                core::mem::transmute(this)
876            }
877
878            unsafe fn shorten_ref<'a>(this: &'_ Self) -> &'_ Self::Parameterized<'a> {
879                core::mem::transmute(this)
880            }
881        }
882
883        impl<'a, $($letter: Stretchable<'a>,)*> Stretchable<'a> for ($($letter,)*) {
884            type Stretched = ($(<$letter as Stretchable<'a>>::Stretched,)*);
885        }
886    };
887}
888
889macro_rules! russian_tuples {
890    ($m: ident, $ty: tt) => {
891        $m!{}
892        $m!{$ty}
893    };
894    ($m: ident, $ty: tt, $($tt: tt),*) => {
895        russian_tuples!{$m, $($tt),*}
896        $m!{$ty, $($tt),*}
897    };
898}
899
900russian_tuples!(impl_tuple, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
901
902unsafe impl<T: Stretched> Stretched for Option<T> {
903    type Parameterized<'a> = Option<T::Parameterized<'a>>;
904
905    unsafe fn lengthen(this: Self::Parameterized<'_>) -> Self {
906        this.map(|t| unsafe { T::lengthen(t) })
907    }
908
909    unsafe fn shorten<'a>(this: Self) -> Self::Parameterized<'a> {
910        this.map(|t| unsafe { T::shorten(t) })
911    }
912
913    unsafe fn shorten_mut<'a>(this: &'_ mut Self) -> &'_ mut Self::Parameterized<'a> {
914        core::mem::transmute(this)
915    }
916
917    unsafe fn shorten_ref<'a>(this: &'_ Self) -> &'_ Self::Parameterized<'a> {
918        core::mem::transmute(this)
919    }
920}
921
922impl<'a, T: Stretchable<'a>> Stretchable<'a> for Option<T> {
923    type Stretched = Option<T::Stretched>;
924}
925
926/// An arena for allocating [`ScopeGuard`]s.
927///
928/// Functions as a memory pool for allocating various trait objects needed for dropping type-erased
929/// [`ElasticGuard`]s. Each time the `scope` method is called, the arena will have some allocations
930/// made; to free these, [`ScopeArena::reset`] should be called.
931#[derive(Debug, Default)]
932pub struct ScopeArena {
933    bump: Bump,
934}
935
936impl ScopeArena {
937    /// Create an empty scope arena.
938    pub fn new() -> Self {
939        Self::default()
940    }
941
942    /// Create a scope within which we can safely loan elastics.
943    ///
944    /// This method *does not* reset the arena afterwards, so if you use it, it is your
945    /// responsibility to reset the `ScopeArena` with [`ScopeArena::reset`] to avoid memory leaks.
946    pub fn scope<'a, F, R>(&'a self, f: F) -> R
947    where
948        F: FnOnce(&mut ScopeGuard<'a>) -> R,
949    {
950        let mut scope_guard = ScopeGuard {
951            bump: &self.bump,
952            buf: Vec::new_in(&self.bump),
953            _phantom: PhantomData,
954        };
955        f(&mut scope_guard)
956    }
957
958    /// Clear memory allocated by the arena, preserving the allocations for reuse.
959    pub fn reset(&mut self) {
960        self.bump.reset();
961    }
962}
963
964trait MakeItDyn {}
965
966impl<T: ?Sized> MakeItDyn for T {}
967
968/// A guard which allows "stashing" [`ElasticGuard`]s for safe loaning.
969///
970/// Bare [`Elastic::loan`] is unsafe, because the returned [`ElasticGuard`] *must* be dropped. A
971/// [`ScopeGuard`] provided by [`ScopeArena::scope`] allows for collecting [`ElasticGuard`]s through
972/// its *safe* [`ScopeGuard::loan`] method, because the [`ScopeArena`] ensures that all loans are
973/// ended at the end of the scope.
974///
975/// As an aside, [`ScopeGuard`] is an excellent example of a type which *cannot* be safely
976/// stretched: the lifetime parameter of the [`ScopeGuard`] corresponds to the accepted lifetime on
977/// the type parameter of [`ScopeGuard::loan`]. As a result, effectively, [`ScopeGuard`] has the
978/// variance of `fn(&'a mut ...)`; it is safe to *lengthen* the lifetimes fed to [`ScopeGuard`], but
979/// absolutely not safe to shorten them!
980pub struct ScopeGuard<'a> {
981    bump: &'a Bump,
982    buf: Vec<ArenaBox<'a, (dyn MakeItDyn + 'a)>, &'a Bump>,
983    // Ensure contravariance.
984    _phantom: PhantomData<fn(&'a mut ())>,
985}
986
987impl<'a> fmt::Debug for ScopeGuard<'a> {
988    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
989        write!(f, "ScopeGuard({} guards held)", self.buf.len())
990    }
991}
992
993impl<'a> ScopeGuard<'a> {
994    /// Loan to an elastic within the lifetime of the scope guard.
995    pub fn loan<T: Stretchable<'a>>(&mut self, elastic: &Elastic<T::Stretched>, value: T) {
996        let boxed_dyn_guard = unsafe {
997            let boxed_guard = ArenaBox::new_in(elastic.loan(value), self.bump);
998            let raw_box = ArenaBox::into_raw(boxed_guard);
999            <ArenaBox<'a, (dyn MakeItDyn + 'a)>>::from_raw(raw_box as *mut (dyn MakeItDyn + 'a))
1000        };
1001        self.buf.push(boxed_dyn_guard);
1002    }
1003}
1004
1005/// An [`Elastic`] which is specialized for the task of loaning `&'a T`s. This is a type synonym for
1006/// `Elastic<StretchedRef<T>>`.
1007pub type ElasticRef<T> = Elastic<StretchedRef<T>>;
1008
1009/// An [`Elastic`] which is specialized for the task of loaning `&'a mut T`s. This is a type synonym
1010/// for `Elastic<StretchedMut<T>>`.
1011pub type ElasticMut<T> = Elastic<StretchedMut<T>>;