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

Documenting FFI Invariants

You will produce technical documentation for users regarding the safety constraints of the C-integrated modules.

In this lesson, you will learn how to articulate the invisible safety requirements that Rust’s compiler cannot enforce when crossing the boundary into C.

When you expose a C library to Rust, the compiler treats foreign functions as unsafe. However, "unsafe" isn't a catch-all; it is a signal that the function requires specific preconditions to be met. Because C lacks Rust's ownership and lifetime models, you must document these hidden FFI Invariants explicitly in your unsafe code documentation.

The Role of # Safety Sections

In Rust, the standard convention is to use a ### Safety section in your doc comments for any unsafe function. This section must clearly state what the caller is responsible for ensuring before the function is invoked.

Consider a function that interacts with a raw pointer from a C API:

/// # Safety
/// 
/// This function calls `ffi::process_data`. 
/// 
/// 1. `ptr` must be non-null and point to a valid, null-terminated buffer.
/// 2. The memory pointed to by `ptr` must remain initialized and immutable 
///    for the duration of the FFI call.
/// 3. The caller must ensure that the underlying C library has been 
///    initialized via `ffi::init()` before calling this function.
pub unsafe fn process_data(ptr: *const c_char) {
    ffi::process_data(ptr);
}

Why Documentation Matters

If you fail to document these invariants, the "contract" between your Rust wrapper and the C code becomes ambiguous. An end-user might accidentally pass a pointer to stack-allocated memory that goes out of scope, or fail to satisfy a hidden initialization requirement, leading to undefined behavior that is notoriously difficult to debug.

By treating these invariants as part of the public API, you force the consumer of your code to acknowledge the constraints. If a caller violates an invariant you documented, the fault lies with the caller; if you fail to document an invariant that is necessary for safety, the bug is in your wrapper. Always assume the user does not know the internal state requirements of the underlying C library.

Key Takeaway: Every unsafe FFI function must include a ### Safety section that explicitly lists the preconditions a caller must satisfy to prevent undefined behavior.