Macro cryo::cryo[][src]

macro_rules! cryo {
    () => { ... };
    ($(#[$attr : meta]) * let $name : ident : $Cryo : ident < $t : ty
 $(, $Lock : ty) ? > = $init : expr ; $($rest : tt) *) => { ... };
    ($(#[$attr : meta]) * let $name : ident : $Cryo : ident < $t : ty
 $(, $Lock : ty) ? > = $init : expr) => { ... };
}
👎 Deprecated:

cryo! is unsound when used inside async fn and will be removed in a future version

Expand description

Construct a Cryo or CryoMut and bind it to a local variable.

Safety

Don’t use. This macro is unsound when used inside an async fn. This macro doesn’t require unsafe { ... } merely not to cause breakage.

The unsafety is demonstrated in the following code:

#![allow(deprecated)]
use cryo::cryo;
use std::{
    sync::atomic::{AtomicBool, Ordering},
    future::Future,
    task::Context,
};

// For demonstration purposes, we want to stop execution when an undefined
// behavior is about occur. To this end, we track the `UserType` object's
// aliveness with this flag.
static IS_USER_TYPE_ALIVE: AtomicBool = AtomicBool::new(true);

struct UserType(u32);

impl Drop for UserType {
    fn drop(&mut self) {
        IS_USER_TYPE_ALIVE.store(false, Ordering::Relaxed);
    }
}

// Let there be a `UserType`.
let user_type = UserType(42);

let mut borrow = None;

// Apply `cryo!` on it inside an `async` block.
let mut fut = Box::pin(async {
    cryo!(let cryo: Cryo<_, cryo::SyncLock> = &user_type);

    // Leak `borrow` to the outer environment
    borrow = Some(cryo.borrow());

    // This `Future` will get stuck here. Furthermore, we `forget` this
    // `Future`, so `cryo`'s destructor will never run.
    std::future::pending::<()>().await
});

// Run the `Future` until it stalls
fut.as_mut().poll(&mut Context::from_waker(&futures::task::noop_waker()));

// Forget the `Future`. The compiler thinks `user_type` is not borrowed,
// but in fact `cryo`, which is borrowing it, is still on memory.
std::mem::forget(fut);

// And `user_type` is gone. Now `cryo` is dangling.
drop(user_type);

// But we can still access the dead `user_type` through `borrow`!
let borrow = borrow.unwrap();
assert!(
    IS_USER_TYPE_ALIVE.load(Ordering::Relaxed),
    "`cryo!` is supposed to keep us safe, isn't it?"  // well, it betrayed us. (panics)
);
assert_eq!(borrow.0, 42);  // UB

Examples

#![allow(deprecated)]
use cryo::cryo;
cryo!(let cryo: Cryo<u8> = &42);
assert_eq!(*cryo.borrow(), 42);
#![allow(deprecated)]
use cryo::cryo;
let mut var = 42;
{
    cryo!(let cryo: CryoMut<u8> = &mut var);
    *cryo.write() = 84;
}
assert_eq!(var, 84);

The lock implementation can be specified by an extra generic argument. It defaults to LocalLock when unspecified.

#![allow(deprecated)]
use cryo::cryo;
use std::thread::spawn;
cryo!(let cryo: Cryo<_, cryo::SyncLock> = &42);
let borrow = cryo.borrow();
spawn(move || {
    assert_eq!(*borrow, 42);
});