Course contentsShow
← Rust
Lesson 166 of 1,9102. The Ownership ModelFree lesson

Memory-Safe C-Interop

Convert foreign pointers into safe Rust references with correct lifetime annotations.

In this lesson, you will learn how to safely bridge the gap between raw C pointers and Rust’s strict ownership model using explicit lifetime annotations.

When working with C, you often receive raw pointers (*const T or *mut T) from an external library. These pointers are "naked"—they carry no information about how long the memory they point to remains valid. To bring these into the safety of Rust, we must manually wrap them in references (&T) and provide the compiler with a lifetime that guarantees the memory won't be freed while we are using it.

Binding the Lifetime to the Pointer

If you receive a raw pointer from C, you are effectively asserting to the compiler that you have verified the memory’s validity. To turn this into a safe reference, you must use an unsafe block and assign a lifetime that matches the scope of the underlying C resource.

Consider a C function that returns a pointer to a configuration struct:

// Assume this comes from a C library
extern "C" {
    fn get_config() -> *const Config;
}

// We wrap it in a safe function
fn get_safe_config<'a>() -> &'a Config {
    unsafe {
        let ptr = get_config();
        // We cast the pointer to a reference, 
        // explicitly telling Rust it lives for 'a
        &*ptr 
    }
}

By adding the 'a lifetime, you are telling the Rust compiler: "I promise this pointer will remain valid for the duration of 'a." If you try to hold onto this reference longer than the C resource lives, the borrow checker will trigger an error, preventing a use-after-free bug.

The Responsibility of the Developer

Because C has no concept of Rust’s "Borrow Checker," the compiler cannot verify your promise. You must ensure that the C library guarantees the pointer's validity for the entire duration of the lifetime you specified. If the C library frees that memory (e.g., via a free() call) while your Rust code is still using the reference, you have violated memory safety. Your goal is to map the C library's documented memory guarantees onto Rust's lifetime syntax.

Key Takeaway: By wrapping raw pointers in references with explicit lifetimes, you move memory safety guarantees from your own manual tracking into the hands of the Rust compiler.