https://github.com/yvt/cryo
&'a T → impl Deref + 'static
https://github.com/yvt/cryo
lifetime rust
Last synced: 2 months ago
JSON representation
&'a T → impl Deref + 'static
- Host: GitHub
- URL: https://github.com/yvt/cryo
- Owner: yvt
- License: apache-2.0
- Created: 2018-11-13T04:17:35.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2021-10-26T04:25:04.000Z (almost 4 years ago)
- Last Synced: 2025-07-28T19:48:18.731Z (2 months ago)
- Topics: lifetime, rust
- Language: Rust
- Homepage: https://crates.io/crates/cryo
- Size: 98.6 KB
- Stars: 8
- Watchers: 2
- Forks: 1
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE-APACHE
Awesome Lists containing this project
README
![]()
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](https://crates.io/crates/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.[readers-writer locks]: https://en.wikipedia.org/wiki/Readers–writer_lock
¹ `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):```rust
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 = cryo.borrow();
let closure: Box =
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):
```rust
use std::{thread::spawn, pin::Pin};let cell: usize = 42;
// This time we are specifying the lock implementation
with_cryo((&cell, lock_ty::()), |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 = 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`:
```rust
with_cryo((&mut cell, lock_ty::()), |cryo_mut| {
// Borrow `cryo_mut` and move it into a `'static` closure.
let mut borrow: CryoMutWriteGuard = 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:
```rust
// 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::()), |cryo| cryo.borrow());
``````rust
// 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>`, 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.[`spin::RawRwLock`]: https://docs.rs/spin/0.9.0/spin/type.RwLock.html
[`parking_lot::RawRwLock`]: https://docs.rs/parking_lot/0.11.1/parking_lot/struct.RawRwLock.html
[#32976]: https://github.com/rust-lang/rust/issues/32976### Overhead
`Cryo`'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](https://en.wikipedia.org/wiki/Cryopreservation).
License: MIT/Apache-2.0