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>>;