Crate cryo[][src]

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 CryoRef temporarily in a std::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 to Cryo’s constructor, the borrowed type is CryoRef<NonStaticType<'b>>, which is still partially bound to the original lifetime.

Details

Feature flags

  • std (enabled by default) enables SyncLock.

  • lock_api enables the blanket implementation of Lock on all types implementing lock_api::RawRwLock, such as spin::RawRwLock and parking_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

An implementation of Lock that uses atomic operations. Panics on borrow failure.

A cell-like type that enforces the lifetime restriction of its borrowed value at runtime.

A cell-like type that enforces the lifetime restriction of its borrowed value at runtime.

The read lock guard type of CryoMut.

The write lock guard type of CryoMut.

A single-thread implementation of Lock. Panics on borrow failure.

Marker type to specify the Lock type to use with with_cryo.

An implementation of Lock that 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

A trait for readers-writer locks.

The trait for types that can be wrapped with Cryo or CryoMut.

Functions

Construct a LockTyMarker.

Call a given function with a constructed Cryo or CryoMut.

Type Definitions

The lock guard type of Cryo. This is currently a type alias but might change in a future.