Macro deferred_reference::defer[][src]

macro_rules! defer {
    ($place:expr) => { ... };
}

An unsafe macro to create a deferred immutable reference (i.e. a Deferred<&T>) to a place, without creating an intermediate reference. See the documentation at Deferred explaining the constructors for deferred immutable references for safe alternatives.

Use of this macro is not recommended, because it’s a lot more unsafe than obtaining a Deferred<&T> through a type that implements the Defer trait. Use Defer::defer or Deferred::from(&T) for a safe alternative instead.

The only use case for this macro is when you can’t create a reference to a place and also can’t use UnsafeCell to achieve interior mutability. However, this is a rare case and requires advanced knowledge of unsafe Rust.

Example

#[macro_use]
extern crate deferred_reference;
use deferred_reference::Deferred;
fn main() {
    let buffer = [0u8; 1024];
    // works for deferred references to slices:
    let deferred: Deferred<&[u8]> =  unsafe { defer!(buffer) };
    assert_eq!(buffer[0], deferred[0]);
    // and works also for deferred references to arrays:
    let deferred: Deferred<&[u8; 1024]> =  unsafe { defer!(buffer) };
    assert_eq!(buffer[0], deferred[0]);
}

Safety

This macro is very unsafe and should only be used if there is no other safe way to obtain a deferred reference. See the Defer trait for the preferred way to create an immutable deferred reference. When using this macro, the caller must uphold the following guarantees:

  • When dereferencing the Deferred, the Rust alias rules must be upheld at all times. E.g. don’t create mutable and immutable references to the same place (these may not partially overlap either).
  • The place must be properly aligned and initialized.
  • The caller must ensure that the invariant of the returned Deferred is upheld.
  • The place must not be moved or dropped for as long as the returned Deferred is in use.
  • No mutable references to the place may be created as long as the Deferred is in use. This will invalidate the Deferred.

Here is an example that will cause undefined behavior to illustrate how unsafe this macro is. The compiler will happilly compile this and not give any warning:

#[macro_use]
extern crate deferred_reference;
fn main() {
    let mut buffer = [0u8; 1024];
    let deferred = unsafe { defer!(buffer) };
    buffer[0] = 42; // this implicitly creates a temporary mutable reference to `buffer`
    // `deferred` is now invalidated 
    // this is undefined behavior, even though the lifetimes of
    // the immutable and mutable reference don't overlap:
    assert_eq!(buffer[0], deferred[0]);
}

This kind of pitfalls are not possible with instances of Deferred<&T> obtained through Defer::defer, because then the Rust compiler can detect these violations due to the bounded lifetime of Deferred<&T>. In this case the compiler will warn you before-hand:

use deferred_reference::Defer;
use core::cell::UnsafeCell;
let mut buffer = UnsafeCell::new([0u8; 1024]);
let deferred = buffer.defer(); // note the absence of the unsafe block!
// the next line will cause the compilation to fail with the error:
// "cannot borrow `buffer` as mutable because it is also borrowed as immutable"
buffer.get_mut()[0] = 42;
assert_eq!(0, deferred[0]); // assures `deferred` is in use until here

Caveat

The lifetime for the returned Deferred is inferred from its usage. To prevent accidental misuse, it’s suggested to tie the lifetime to whichever source lifetime is safe in the context, such as by providing a helper function taking the lifetime of a host value, or by explicit annotation. However, this can get very complicated and very unsafe real fast, here is an example of how this could end up looking like (accompanied by the obligatory “do not try this at home, kids!”):

#[macro_use]
extern crate deferred_reference;
use deferred_reference::Deferred;
use core::marker::PhantomData;
fn shorten_lifetime<'a, 'b: 'a>(a: &'a PhantomData<[u8]>, b: Deferred<&'b [u8]>)
    -> (&'a PhantomData<[u8]>, Deferred<&'a [u8]>)
{
    // SAFETY: shortening the lifetime of 'b to 'a is always safe
    unsafe { core::mem::transmute((a, b)) }
}
fn borrow_mut<'a>(_accountant: &'a mut PhantomData<[u8]>, borrow_mut: &'a mut [u8])
    -> &'a mut [u8]
{
    borrow_mut
}
macro_rules! get_deferred {
    ($accountant:ident, $place:expr) => {
        shorten_lifetime(&$accountant, defer!($place)).1
    };
}
macro_rules! borrow_mut {
    ($accountant:ident, $place:expr) => {
        borrow_mut(&mut $accountant, &mut $place)
    };
}
fn main() {
    let mut buffer = [0u8; 1024];
    // the `accountant` acts as a compile-time semaphore through the Rust borrow rules.
    let mut accountant = PhantomData::<[u8]>;
    // SAFETY: we promise to only use the `get_deferred` and `borrow_mut` macros.
    // SAFETY: this is safe, because we don't take out explicit borrows like
    // SAFETY: `&mut buffer` which are not tracked by our `accountant`.
    let deferred: Deferred<&[u8]> = unsafe { get_deferred!(accountant, buffer) };
    assert_eq!(0, deferred[0]);
    // the next line will give an error at compile-time:
    // "cannot borrow `accountant` as mutable because it is also borrowed as immutable"
    let ref_mut: &mut [u8] = borrow_mut!(accountant, buffer); // mutably borrows
    assert_eq!(0, deferred[0]); // asserts that `deferred` is in use until here
}