Macro deferred_reference::defer_mut[][src]

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

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

Use of this macro is not recommended, because it’s a lot more unsafe than obtaining a Deferred<&mut T> through a type that implements the DeferMut trait. Use DeferMut::defer_mut or Deferred::from(&mut T) for a safer 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 mut buffer = [0u8; 1024];
    let deferred: Deferred<&mut [u8]> =  unsafe { defer_mut!(buffer) };
    assert_eq!(buffer[0], deferred[0]);
    // works also for references to arrays:
    let deferred: Deferred<&mut [u8; 1024]> =  unsafe { defer_mut!(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 mutable reference. See the DeferMut trait for the preferred way to create an deferred mutable 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 explicit references to the place may be created as long as the Deferred is in use. This will invalidate the Deferred.
  • Any other instances of Deferred that point to the same location, must be reborrowed from the original deferred mutable reference. This is possible using Deferred::clone_unchecked and Deferred::into_ref. Any other deferred references will becomed invalidated as soon as the deferred mutable reference is dereferenced (unless its target contents are inside an UnsafeCell).

Here is an example that will trigger undefined behavior, in order to illustrate how unsafe this macro is:

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

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, see the defer macro for an example of how to do this without creating an intermediate reference.

How can this be safely used together with the defer! macro?

As mentioned above under section “Safety”, dereferencing a Deferred<&mut T> will invalidate any other Deferred instances which are not re-borrowed, even the ones created by the defer macro (Deferred<&T> instances returned by the defer macro do not constitute as re-borrows). This means that the defer macro is only safe to use together with the defer_mut macro if you take special care to always call defer! again to refresh its pointer, after a mutable reference has been given out through dereferencing the Deferred<&mut T>. For example, this is definately considered undefined behavior:

#[macro_use]
extern crate deferred_reference;
use deferred_reference::Deferred;
fn main() {
    let mut buffer = [0u8; 1024];
    // SAFETY: what we are about to do is very unsafe!
    let mut deferred_mut: Deferred<&mut [u8]> = unsafe { defer_mut!(buffer) };
    let deferred: Deferred<&[u8]> =  unsafe { defer!(buffer) };
    assert_eq!(0, deferred_mut[0]);
    assert_eq!(0, deferred[0]); // so far so good, no UB yet...
    deferred_mut[0] = 42; // this implicity creates a mutable reference to `buffer`
    // `deferred` is now invalidated!
    assert_eq!(42, deferred_mut[0]); // this is not yet UB...
    assert_eq!(42, deferred[0]); // this is UB!
}

The undefined behavior can be side-stepped if the deferred mutable reference is re-borrowed, like so:

#[macro_use]
extern crate deferred_reference;
use deferred_reference::Deferred;
fn main() {
    let mut buffer = [0u8; 1024];
    // SAFETY: this is safe because we reborrow `deferred_mut` and
    // SAFETY: we don't create any overlapping references.
    let mut deferred_mut: Deferred<&mut [u8]> = unsafe { defer_mut!(buffer) };
    let deferred: Deferred<&[u8]> = unsafe { deferred_mut.clone_unchecked().into_ref() };
    assert_eq!(0, deferred_mut[0]);
    assert_eq!(0, deferred[0]);
    deferred_mut[0] = 42; // this implicity creates a mutable reference to `buffer`
    assert_eq!(42, deferred_mut[0]);
    assert_eq!(42, deferred[0]); // but this is not UB thanks the re-borrow
}

If the calls to the defer macro are timed well, then it is possible to combine the two macros without running into undefined behavior:

#[macro_use]
extern crate deferred_reference;
use deferred_reference::Deferred;
fn main() {
    let mut buffer = [0u8; 1024];
    // SAFETY: this is safe, because we create new deferred references after
    // SAFETY: dereferencing `deferred_mut` into an actual mutable reference.
    let mut deferred_mut: Deferred<&mut [u8]> = unsafe { defer_mut!(buffer) };
    let mut deferred: Deferred<&[u8]> = unsafe { defer!(buffer) };
    assert_eq!(0, deferred_mut[0]);
    assert_eq!(0, deferred[0]);
    deferred_mut[0] = 42; // this implicity creates a temporary mutable reference to `buffer`
    // `deferred` is now invalidated! we refresh it again:
    deferred = unsafe { defer!(buffer) };
    assert_eq!(42, deferred_mut[0]);
    // this is not UB, because the mutable reference did not overlap
    // with the re-creation of `deferred`:
    assert_eq!(42, deferred[0]); 
}

The previous 3 examples consist of very unsafe Rust and should not be attempted unless there exists a specific reason why this is needed and the implementor has intricate knowledge about raw pointer management in Rust. In all other cases, using the DeferMut::defer_mut is the much better alternative to the defer_mut macro.