Crate cryo

source ·
Expand description

Extend the lifetime of a reference. Safely.

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:

This works by, when a Cryo is dropped, blocking the current thread until all references to the contained value are dropped so that none of them can outlive the cell.

The constructor of Cryo is marked as unsafe because it’s easy to break various assumptions essential to memory safety if Cryo values are not handled properly. Utility functions with_cryo and with_cryo_mut ensure safety by providing access to Cryo values in a controlled way.

Examples

with_cryo and Cryo:

use std::thread::spawn;

let cell: usize = 42;

with_cryo(&cell, |cryo: &Cryo<usize>| {
    // Borrow `cryo` and move it into a `'static` closure.
    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, `with_cryo` will not return
    // until the thread we just spawned completes execution.
});

with_cryo_mut and CryoMut:

with_cryo_mut(&mut cell, |cryo_mut: &CryoMut<usize>| {
    // 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, `with_cryo_mut` will not
    // return until the thread we just spawned completes execution.
});
assert_eq!(cell, 1);

Don’t do this:

// The following statement will deadlock because it attempts to drop
// `Cryo` while a `CryoRef` is still referencing it
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 the Cryo’s constructor, the borrowed type is CryoRef<NonStaticType<'b>>, which is still partially bound to the original lifetime.

Details

Feature flags

  • parking_lot — Specifies to use parking_lot instead of std::sync.

Overhead

Cryo<T> incurs moderate overhead due to the uses of Mutex and Condvar. This can be alleviated somewhat by using the parking_lot feature flag.

Nomenclature

From cryopreservation.

Structs

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.

Functions

Call a given function with a constructed Cryo.
Call a given function with a constructed CryoMut.

Type Definitions

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