Course contentsShow
← Rust
Lesson 1393 of 1,91012. Unsafe Rust and Low-Level AccessFree lesson

The Contract of Unsafe Functions

You will write documentation that explicitly details the prerequisites for callers to uphold memory safety.

In this lesson, you will learn how to formally define and document the safety requirements that callers must satisfy before invoking an unsafe function.

Defining the "Contract"

When you mark a function as unsafe, you are essentially shifting the responsibility of memory safety from the compiler to the developer. Because the compiler can no longer guarantee that the function won't cause Undefined Behavior (UB), the function must come with a contract: a precise list of prerequisites that the caller must guarantee are true.

If the caller fails to meet these requirements, the program enters an invalid state, and the guarantees of Rust are nullified.

How to Document the Contract

In idiomatic Rust, these requirements are documented using a section titled # Safety in the function's doc comments. This section acts as the binding agreement between you (the implementer) and the caller.

Consider this example:

/// Returns the first element of a slice without bounds checking.
///
/// # Safety
///
/// The caller must ensure that the slice `data` is not empty. 
/// If `data` is empty, this function will trigger undefined behavior 
/// by accessing memory out of bounds.
pub unsafe fn get_first_unchecked<T>(data: &[T]) -> &T {
    data.get_unchecked(0)
}

Writing Effective Requirements

A solid safety contract must be unambiguous. Avoid vague language. When writing your # Safety section, ask yourself:

  1. Which arguments are involved? (e.g., "The pointer ptr must be non-null.")
  2. What state must they be in? (e.g., "The pointer must be aligned to the type T.")
  3. What must remain true? (e.g., "The memory pointed to must remain initialized for the duration of the reference.")

By clearly documenting these constraints, you allow users of your code to reason about their own unsafe blocks. They can verify that their usage respects your contract, making the overall codebase significantly easier to audit for potential memory corruption bugs.

Key Takeaway: An unsafe function is a liability; its # Safety documentation is the only thing standing between the caller and undefined behavior.