Expand description
Requires Rust 1.34.0 or later.
This crate provides a cell-like type Cryo that is similar to RefCell
except that it constrains the lifetime of its borrowed value
through a runtime check mechanism, erasing the compile-time lifetime
information. The lock guard CryoRef created from Cryo is
'static and therefore can be used in various situations that require
'static types, including:
- Storing
CryoReftemporarily in astd::any::Any-compatible container. - Capturing a reference to create a Objective-C block.
This works by, when a Cryo is dropped, not letting the current thread’s
execution move forward (at least¹) until all references to the expiring
Cryo are dropped so that none of them can outlive the Cryo.
This is implemented by readers-writer locks under the hood.
¹ SyncLock blocks the current thread’s execution on lock failure.
LocalLock, on the other hand, panics because it’s designed for
single-thread use cases and would deadlock otherwise.
§Examples
with_cryo, Cryo, and LocalLock (single-thread lock
implementation, used by default):
use std::{thread::spawn, pin::Pin};
let cell: usize = 42;
// `with_cryo` uses `LocalLock` by default
with_cryo(&cell, |cryo: Pin<&Cryo<'_, usize, _>>| {
// Borrow `cryo` and move it into a `'static` closure.
let borrow: CryoRef<usize, _> = cryo.borrow();
let closure: Box<dyn Fn()> =
Box::new(move || { assert_eq!(*borrow, 42); });
closure();
drop(closure);
// Compile-time lifetime works as well.
assert_eq!(*cryo.get(), 42);
// When `cryo` is dropped, it will block until there are no other
// references to `cryo`. In this case, the program will leave
// this block immediately because `CryoRef` has already been dropped.
});with_cryo, Cryo, and SyncLock (thread-safe lock implementation):
use std::{thread::spawn, pin::Pin};
let cell: usize = 42;
// This time we are specifying the lock implementation
with_cryo((&cell, lock_ty::<SyncLock>()), |cryo| {
// Borrow `cryo` and move it into a `'static` closure.
// `CryoRef` can be sent to another thread because
// `SyncLock` is thread-safe.
let borrow: CryoRef<usize, _> = cryo.borrow();
spawn(move || { assert_eq!(*borrow, 42); });
// Compile-time lifetime works as well.
assert_eq!(*cryo.get(), 42);
// When `cryo` is dropped, it will block until there are no other
// references to `cryo`. In this case, the program will not leave
// this block until the thread we just spawned completes execution.
});with_cryo, CryoMut, and SyncLock:
with_cryo((&mut cell, lock_ty::<SyncLock>()), |cryo_mut| {
// Borrow `cryo_mut` and move it into a `'static` closure.
let mut borrow: CryoMutWriteGuard<usize, _> = cryo_mut.write();
spawn(move || { *borrow = 1; });
// When `cryo_mut` is dropped, it will block until there are no other
// references to `cryo_mut`. In this case, the program will not leave
// this block until the thread we just spawned completes execution
});
assert_eq!(cell, 1);Don’t do these:
// The following statement will DEADLOCK because it attempts to drop
// `Cryo` while a `CryoRef` is still referencing it, and `Cryo`'s
// destructor will wait for the `CryoRef` to be dropped first (which
// will never happen)
let borrow = with_cryo((&cell, lock_ty::<SyncLock>()), |cryo| cryo.borrow());// The following statement will ABORT because it attempts to drop
// `Cryo` while a `CryoRef` is still referencing it, and `Cryo`'s
// destructor will panic, knowing no amount of waiting would cause
// the `CryoRef` to be dropped
let borrow = with_cryo(&cell, |cryo| cryo.borrow());§Caveats
- While it’s capable of extending the effective lifetime of a reference,
it does not apply to nested references. For example, when
&'a NonStaticType<'b>is supplied toCryo’s constructor, the borrowed type isCryoRef<NonStaticType<'b>>, which is still partially bound to the original lifetime.
§Details
§Feature flags
-
std(enabled by default) enablesSyncLock. -
lock_apienables the blanket implementation ofLockon all types implementinglock_api::RawRwLock, such asspin::RawRwLockandparking_lot::RawRwLock. -
atomic(enabled by default) enables features that require full atomics, which is not supported by some targets (detecting such targets is still unstable (#32976)). This feature will be deprecated after the stabilization of #32976.
§Overhead
Cryo<T, SyncLock>’s creation, destruction, borrowing, and unborrowing
each take one or two atomic operations in the best cases.
Neither of SyncLock and LocalLock require dynamic memory allocation.
§Nomenclature
From cryopreservation.
Structs§
- Atomic
Lock atomic - An implementation of
Lockthat uses atomic operations. Panics on borrow failure. - Cryo
- A cell-like type that enforces the lifetime restriction of its borrowed value at runtime.
- CryoMut
- A cell-like type that enforces the lifetime restriction of its borrowed value at runtime.
- Cryo
MutRead Guard - The read lock guard type of
CryoMut. - Cryo
MutWrite Guard - The write lock guard type of
CryoMut. - Local
Lock - A single-thread implementation of
Lock. Panics on borrow failure. - Lock
TyMarker - Marker type to specify the
Locktype to use withwith_cryo. - NoSend
Marker - Send
Marker - Sync
Lock std - An implementation of
Lockthat uses the synchronization facility provided by::std. Lock operations are tied to the creator thread, but unlock operations can be done in any threads. Blocks the current thread on borrow failure.
Traits§
- Lock
- A trait for readers-writer locks.
- With
Cryo - The trait for types that can be wrapped with
CryoorCryoMut.
Functions§
- lock_ty
- Construct a
LockTyMarker. - with_
cryo - Call a given function with a constructed
CryoorCryoMut.