pub struct Elastic<T: Stretched> { /* private fields */ }
Expand description
A container for a stretched value.
This acts a bit like Arc<AtomicRefCell<Option<T>>>
, through its Clone
behavior and borrowing
methods, but the similarity ends there. The only way to put data into this type is through
Elastic::loan
, which allows you to safely loan some Stretchable
, non-'static
type, to
an Elastic
carrying the corresponding Stretched
type. As Stretched
requires
'static
, Elastic<T>
is always 'static
and can be used to access non-'static
types
safely from contexts which require 'static
(such as dynamic typing with Any
or the
hv-alchemy
crate.) The lifetime is preserved by a scope guard, ElasticGuard
, which is
provided when a value is loaned and which revokes the loan when it is dropped or has the loaned
value forcibly taken back by ElasticGuard::take
. In most cases, you won’t actually see these
ElasticGuard
s as they’ll be created and stashed away by a ScopeGuard
, which you should
be using to perform all your loans.
Elastic<T>
is thread-safe. However, internally, it uses an
AtomicRefCell
, so if you violate borrowing invariants, you will have
a panic on your hands. This goes likewise for taking the value back w/ ElasticGuard
or
dropping the guard: the guard will panic if it cannot take back the value.
§Soundness
As long as you fulfill its requirements with respect to its guards, Elastic
is sound in all
normal modes of operation. However, in the case of a panic across threads, bad things can
happen:
- If an elastic exists on one thread and is being loaned to with a guard on another thread, and
the thread holding the guard panicks, it is unclear what will/should happen to the other
thread. Most likely, the thread holding the guard will attempt to drop the guard, which will
then cause the guard’s
Drop
impl to panic, which will then cause an abort (as panicking during a panick will cause an abort.) However, if the panic starts in the guard’sDrop
impl, and the guard is handling a reference which has been loaned to it, it is possible that the original owner of the data being borrowed there could be dropped, while the other thread is holding an open borrow on a reference to that data. Since this lib isno_std
, we cannotstd::process::abort()
. We may be able to consider this soundness hole plugged by marking this type!UnwindSafe
or requiring that stretched types beUnwindSafe
; this is an open question.
So in short, unless you’re worried about recovering from panicks across threads where the thread
panicking is the one that originally owned the data (and most likely the parent thread),
Elastic
is thought to be completely sound.
Implementations§
Source§impl<T: Stretched> Elastic<T>
impl<T: Stretched> Elastic<T>
Sourcepub fn new() -> Self
pub fn new() -> Self
Create an empty Elastic<T>
.
Sourcepub fn borrow<'a>(
&'a self,
) -> AtomicRef<'a, <T::Parameterized<'a> as Deref>::Target>where
T::Parameterized<'a>: Deref,
pub fn borrow<'a>(
&'a self,
) -> AtomicRef<'a, <T::Parameterized<'a> as Deref>::Target>where
T::Parameterized<'a>: Deref,
Immutably borrow the loaned value. Panicks if the elastic is already mutably borrowed or if it was never loaned to/any loans have expired.
This method assumes that the stretchable type is a reference or smart pointer and can be
immediately dereferenced; if that’s not the case, please use try_borrow_as_parameterized
.
Sourcepub fn borrow_mut<'a>(
&'a self,
) -> AtomicRefMut<'a, <T::Parameterized<'a> as Deref>::Target>where
T::Parameterized<'a>: DerefMut,
pub fn borrow_mut<'a>(
&'a self,
) -> AtomicRefMut<'a, <T::Parameterized<'a> as Deref>::Target>where
T::Parameterized<'a>: DerefMut,
Mutably borrow the loaned value. Panicks if the elastic is already borrowed or if it was never loaned to/any loans have expired.
This method assumes that the stretchable type is a reference or smart pointer and can be
immediately dereferenced; if that’s not the case, please use
try_borrow_as_parameterized_mut
.
Sourcepub fn borrow_arc<'a>(
&'a self,
) -> ArcRef<<T::Parameterized<'a> as Deref>::Target, Option<T>>where
T::Parameterized<'a>: Deref,
pub fn borrow_arc<'a>(
&'a self,
) -> ArcRef<<T::Parameterized<'a> as Deref>::Target, Option<T>>where
T::Parameterized<'a>: Deref,
Immutably borrow the loaned value through a reference-counted guard. Panicks if the elastic is already mutably borrowed or if it was never loaned to/any loans have expired.
This method assumes that the stretchable type is a reference or smart pointer and can be
immediately dereferenced; if that’s not the case, please use try_borrow_as_parameterized_arc
.
Sourcepub fn borrow_arc_mut<'a>(
&'a self,
) -> ArcRefMut<<T::Parameterized<'a> as Deref>::Target, Option<T>>where
T::Parameterized<'a>: DerefMut,
pub fn borrow_arc_mut<'a>(
&'a self,
) -> ArcRefMut<<T::Parameterized<'a> as Deref>::Target, Option<T>>where
T::Parameterized<'a>: DerefMut,
Mutably borrow the loaned value through a reference-counted guard. Panicks if the elastic is already borrowed or if it was never loaned to/any loans have expired.
This method assumes that the stretchable type is a reference or smart pointer and can be
immediately dereferenced; if that’s not the case, please use
try_borrow_as_parameterized_arc_mut
.
Sourcepub fn try_borrow<'a>(
&'a self,
) -> Result<AtomicRef<'a, <T::Parameterized<'a> as Deref>::Target>, BorrowError>where
T::Parameterized<'a>: Deref,
pub fn try_borrow<'a>(
&'a self,
) -> Result<AtomicRef<'a, <T::Parameterized<'a> as Deref>::Target>, BorrowError>where
T::Parameterized<'a>: Deref,
Immutably borrow the loaned value. Returns Err
if the elastic is already mutably borrowed
or if it was never loaned to/any loans have expired.
This method assumes that the stretchable type is a reference or smart pointer and can be
immediately dereferenced; if that’s not the case, please use try_borrow_as_parameterized
.
Sourcepub fn try_borrow_arc<'a>(
&'a self,
) -> Result<ArcRef<<T::Parameterized<'a> as Deref>::Target, Option<T>>, BorrowError>where
T::Parameterized<'a>: Deref,
pub fn try_borrow_arc<'a>(
&'a self,
) -> Result<ArcRef<<T::Parameterized<'a> as Deref>::Target, Option<T>>, BorrowError>where
T::Parameterized<'a>: Deref,
Immutably borrow the loaned value through a reference-counted guard. Returns Err
if the
elastic is already mutably borrowed or if it was never loaned to/any loans have expired.
This method assumes that the stretchable type is a reference or smart pointer and can be
immediately dereferenced; if that’s not the case, please use try_borrow_as_parameterized_arc
.
Sourcepub fn try_borrow_arc_mut<'a>(
&'a self,
) -> Result<ArcRefMut<<T::Parameterized<'a> as Deref>::Target, Option<T>>, BorrowMutError>where
T::Parameterized<'a>: DerefMut,
pub fn try_borrow_arc_mut<'a>(
&'a self,
) -> Result<ArcRefMut<<T::Parameterized<'a> as Deref>::Target, Option<T>>, BorrowMutError>where
T::Parameterized<'a>: DerefMut,
Mutably borrow the loaned value through a reference-counted guard. Returns Err
if the
elastic is already borrowed or if it was never loaned to/any loans have expired.
This method assumes that the stretchable type is a reference or smart pointer and can be
immediately dereferenced; if that’s not the case, please use
try_borrow_as_parameterized_arc_mut
.
Sourcepub fn try_borrow_mut<'a>(
&'a self,
) -> Result<AtomicRefMut<'a, <T::Parameterized<'a> as Deref>::Target>, BorrowMutError>where
T::Parameterized<'a>: DerefMut,
pub fn try_borrow_mut<'a>(
&'a self,
) -> Result<AtomicRefMut<'a, <T::Parameterized<'a> as Deref>::Target>, BorrowMutError>where
T::Parameterized<'a>: DerefMut,
Mutably borrow the loaned value. Returns Err
if the elastic is already borrowed or if it
was never loaned to/any loans have expired.
This method assumes that the stretchable type is a refence or smart pointer and can be
immediately dereferenced; if that’s not the case, please use
try_borrow_as_parameterized_mut
.
Sourcepub fn try_borrow_as_parameterized(
&self,
) -> Result<AtomicRef<'_, T::Parameterized<'_>>, BorrowError>
pub fn try_borrow_as_parameterized( &self, ) -> Result<AtomicRef<'_, T::Parameterized<'_>>, BorrowError>
Attempt to immutably borrow the loaned value, if present.
Sourcepub fn try_borrow_as_parameterized_mut(
&self,
) -> Result<AtomicRefMut<'_, T::Parameterized<'_>>, BorrowMutError>
pub fn try_borrow_as_parameterized_mut( &self, ) -> Result<AtomicRefMut<'_, T::Parameterized<'_>>, BorrowMutError>
Attempt to mutably borrow the loaned value, if present.
Sourcepub fn try_borrow_as_parameterized_arc(
&self,
) -> Result<ArcRef<T::Parameterized<'_>, Option<T>>, BorrowError>
pub fn try_borrow_as_parameterized_arc( &self, ) -> Result<ArcRef<T::Parameterized<'_>, Option<T>>, BorrowError>
Attempt to immutably borrow the loaned value, via a reference-counted guard.
Sourcepub fn try_borrow_as_parameterized_arc_mut(
&self,
) -> Result<ArcRefMut<T::Parameterized<'_>, Option<T>>, BorrowMutError>
pub fn try_borrow_as_parameterized_arc_mut( &self, ) -> Result<ArcRefMut<T::Parameterized<'_>, Option<T>>, BorrowMutError>
Attempt to mutably borrow the loaned value, via a reference-counted guard..
Sourcepub unsafe fn loan<'a>(
&self,
t: T::Parameterized<'a>,
) -> ElasticGuard<'a, T::Parameterized<'a>>
pub unsafe fn loan<'a>( &self, t: T::Parameterized<'a>, ) -> ElasticGuard<'a, T::Parameterized<'a>>
Loan a stretchable value to this Elastic
in exchange for a guard object which ends the
loan when the value is taken back or when the guard is dropped.
Panics if there is already a loan in progress to this Elastic
.
§Safety
The guard must have its destructor run by the end of its lifetime, either by dropping it
or using ElasticGuard::take
. Calling core::mem::forget
on an ElasticGuard
is
considered instant undefined behavior, as it leaves an Elastic
in a state which is not
well-defined and potentially contains a stretched value which is long past the end of its
life, causing a use-after-free.
Trait Implementations§
Source§impl<T: Stretched, U: ?Sized> NonBlockingGuardedBorrow<U> for Elastic<T>where
for<'a> T::Parameterized<'a>: Borrow<U>,
impl<T: Stretched, U: ?Sized> NonBlockingGuardedBorrow<U> for Elastic<T>where
for<'a> T::Parameterized<'a>: Borrow<U>,
Source§type Guard<'a> = AtomicRef<'a, U>
where
U: 'a
type Guard<'a> = AtomicRef<'a, U> where U: 'a
std::cell::Ref<'a, T>
.)Source§type BorrowError<'a> = BorrowError
where
U: 'a
type BorrowError<'a> = BorrowError where U: 'a
Source§fn try_nonblocking_guarded_borrow(
&self,
) -> Result<Self::Guard<'_>, Self::BorrowError<'_>>
fn try_nonblocking_guarded_borrow( &self, ) -> Result<Self::Guard<'_>, Self::BorrowError<'_>>
Source§impl<T: Stretched, U: ?Sized> NonBlockingGuardedBorrowMut<U> for Elastic<T>where
for<'a> T::Parameterized<'a>: BorrowMut<U>,
impl<T: Stretched, U: ?Sized> NonBlockingGuardedBorrowMut<U> for Elastic<T>where
for<'a> T::Parameterized<'a>: BorrowMut<U>,
Source§type GuardMut<'a> = AtomicRefMut<'a, U>
where
U: 'a
type GuardMut<'a> = AtomicRefMut<'a, U> where U: 'a
std::cell::RefMut<'a, T>
.)Source§type BorrowMutError<'a> = BorrowMutError
where
U: 'a
type BorrowMutError<'a> = BorrowMutError where U: 'a
Source§fn try_nonblocking_guarded_borrow_mut(
&self,
) -> Result<Self::GuardMut<'_>, Self::BorrowMutError<'_>>
fn try_nonblocking_guarded_borrow_mut( &self, ) -> Result<Self::GuardMut<'_>, Self::BorrowMutError<'_>>
Source§impl<T: Stretched, U: ?Sized> NonBlockingGuardedMutBorrowMut<U> for Elastic<T>where
for<'a> T::Parameterized<'a>: BorrowMut<U>,
impl<T: Stretched, U: ?Sized> NonBlockingGuardedMutBorrowMut<U> for Elastic<T>where
for<'a> T::Parameterized<'a>: BorrowMut<U>,
Source§type MutGuardMut<'a> = AtomicRefMut<'a, U>
where
U: 'a
type MutGuardMut<'a> = AtomicRefMut<'a, U> where U: 'a
std::sync::RwLockWriteGuard<'a, T>
.)