#[require_unsafe_in_body]
A procedural macro attribute to make an unsafe fn still require unsafe blocks in its body.
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):
/// Swaps two values of a mutable slice, without checking that the indices are valid.
pub
unsafe // narrow contract
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:
-
we are performing unchecked indexing, which would break if
i ≥ lenorj ≥ len; -
we are asserting that
at_iandat_jdo not alias, which would break ifi = j.
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:
/// 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()`
pub
unsafe // narrow contract
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 intrinsicunsafe-ness (hygiene) of anunsafe fnbody, thus making it necessary to useunsafescopes inside.
Example
The code
extern crate require_unsafe_in_body;
/// Swaps two values of a mutable slice, without checking that the indices are valid.
pub
unsafe // narrow contract
yields the following compiler error:
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
--> example.rs:11:24
|
11 | let at_i: *mut T = slice.get_unchecked_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 at_j: *mut T = slice.get_unchecked_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::swap_nonoverlapping(at_i, 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
-
Add this to your
Cargo.toml:[] = "0.2.0" -
Add this to your
lib.rs(ormain.rs):extern crate require_unsafe_in_body; -
You can then decorate:
-
functions, with the
#[require_unsafe_in_body]attribute; -
methods, with the
#[require_unsafe_in_bodies]attribute applied to the enclosingimplortraitblock.
-