#[require_unsafe_in_body]

A procedural macro attribute to make an unsafe fn still require unsafe blocks in its body.

Latest version Documentation License

Motivation

Imagine having a function with a narrow contract, i.e., a function that can trigger Undefined Behavior in some situations (incorrect inputs or call context). Rust safety design requires that this function be annotated unsafe if it is part of a public API, and even when it is not, it is highly advisable to do so (code will be easier to maintain):

rust /// Swaps two values of a mutable slice, without checking that the indices are valid. pub unsafe // narrow contract fn swap_indices_unchecked<T> (slice: &'_ mut [T], i: usize, j: usize) { let at_i: *mut T = slice.get_unchecked_mut(i); let at_j: *mut T = slice.get_unchecked_mut(j); ::core::ptr::swap_nonoverlapping(at_i, at_j, 1) }

As you can see, when a function is annotated unsafe, the body of the function no longer requires special unsafe blocks around the most subtle things. For instance, in this case, it may not be obvious that there are two distinct unsafe things happening:

Since misusing any of these invariants is wildly unsound, it would be better if we could explicit where each invariant is or may be used:

``rust /// Swaps two values of a mutable slice, without checking that the indices are valid. /// /// # Safety /// /// The indices must be valid: /// /// -i ≠ j /// /// -i < slice.len() /// /// -j < slice.len()`

[allow(unused_unsafe)]

pub unsafe // narrow contract fn swapindicesunchecked (slice: &'_ mut [T], i: usize, j: usize) { let ati: *mut T = unsafe { // Safety: i < slice.len() debugassert!(i < slice.len()); slice.getuncheckedmut(i) }; let atj: *mut T = unsafe { // Safety: j < slice.len() debugassert!(j < slice.len()); slice.getuncheckedmut(j) }; unsafe { // Safety: at_i and at_j do not alias since i ≠ j debugassertne!(i, j); ::core::ptr::swapnonoverlapping(ati, at_j, 1); } } ```

Sadly, since these unsafe blocks are not required, not only do they trigger unused_unsafe warnings, they can also be mistakenly missed without Rust complaining whatsoever.

That's what #[[require_unsafe_in_body]] solves:

#[[require_unsafe_in_body]] "automagically removes" the intrinsic unsafe-ness (hygiene) of an unsafe fn body, thus making it necessary to use unsafe scopes inside.

Example

The code

```rust,compile_fail

[macro_use]

extern crate requireunsafein_body;

/// Swaps two values of a mutable slice, without checking that the indices are valid.

[requireunsafein_body]

pub unsafe // narrow contract fn swapindicesunchecked (slice: &'_ mut [T], i: usize, j: usize) { let ati: *mut T = slice.getuncheckedmut(i); let atj: *mut T = slice.getuncheckedmut(j); ::core::ptr::swapnonoverlapping(ati, at_j, 1); } ```

yields the following compiler error:

```text error[E0133]: call to unsafe function is unsafe and requires unsafe function or block --> example.rs:11:24 | 11 | let ati: *mut T = slice.getunchecked_mut(i); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function | = note: consult the function's documentation for information on how to avoid undefined behavior

error[E0133]: call to unsafe function is unsafe and requires unsafe function or block --> example.rs:12:24 | 12 | let atj: *mut T = slice.getunchecked_mut(j); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function | = note: consult the function's documentation for information on how to avoid undefined behavior

error[E0133]: call to unsafe function is unsafe and requires unsafe function or block --> example.rs:13:5 | 13 | ::core::ptr::swapnonoverlapping(ati, at_j, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function | = note: consult the function's documentation for information on how to avoid undefined behavior

For more information about this error, try rustc --explain E0133. ```

Usage

  1. Add this to your Cargo.toml:

    toml [dependencies] require_unsafe_in_body = "0.2.0"

  2. Add this to your lib.rs (or main.rs):

    ```rust,ignore

    [macro_use]

    extern crate requireunsafein_body; ```

  3. You can then decorate: