Safety comments policy - Standard library developers Guide (original) (raw)
Standard library developers Guide
Using unsafe blocks is often required in the Rust compiler or standard library, but this is not done without rules: each unsafe
block should have a SAFETY:
comment explaining why the block is safe, which invariants are used and must be respected. Below are some examples taken from the standard library:
Inside unsafe elements
This one shows how an unsafe
function can pass the requirements through to its caller with the use of documentation in a # Safety
section while still having more invariants needed that are not required from callers. clippy
has a lint for # Safety
sections by the way.
/// Converts a mutable string slice to a mutable byte slice.
///
/// # Safety
///
/// The caller must ensure that the content of the slice is valid UTF-8
/// before the borrow ends and the underlying `str` is used.
///
/// Use of a `str` whose contents are not valid UTF-8 is undefined behavior.
///
/// ...
pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
// SAFETY: the cast from `&str` to `&[u8]` is safe since `str`
// has the same layout as `&[u8]` (only libstd can make this guarantee).
// The pointer dereference is safe since it comes from a mutable reference which
// is guaranteed to be valid for writes.
unsafe { &mut *(self as *mut str as *mut [u8]) }
}
This example is for a function but the same principle applies to unsafe trait
s like Send or Sync for example, though they have no # Safety
section since their entire documentation is about why they are unsafe
.
Note that in the Rust standard library, unsafe_op_in_unsafe_fn is active and so each unsafe
operation in an unsafe
function must be enclosed in anunsafe
block. This makes it easier to review such functions and to document their unsafe
parts.
Inside safe elements
Inside safe elements, a SAFETY:
comment must not depend on anything from the caller beside properly constructed types and values (i.e, if your function receives a reference that is unaligned or null, it is the caller fault if it fails and not yours).
SAFETY
comments in safe elements often rely on checks that are done before the unsafe
block or on type invariants, like a division by NonZeroU8
would not check for 0
before dividing.
pub fn split_at(&self, mid: usize) -> (&str, &str) {
// is_char_boundary checks that the index is in [0, .len()]
if self.is_char_boundary(mid) {
// SAFETY: just checked that `mid` is on a char boundary.
unsafe { (self.get_unchecked(0..mid), self.get_unchecked(mid..self.len())) }
} else {
slice_error_fail(self, 0, mid)
}
}