Course contentsShow
← Rust
Lesson 65 of 1,9101. Rust Fundamentals and ToolingFree lesson

Calling C Code via FFI

Interoperate with existing C/C++ libraries using foreign function interfaces safely.

In this lesson, you will learn how to bridge the gap between Rust and C by utilizing Foreign Function Interfaces (FFI) to execute existing C code.

The Bridge Between Languages

Rust is designed to be highly interoperable, meaning it can "talk" to libraries written in other languages like C. Because C is a low-level language, the compiler cannot verify the safety of C code as it does with Rust. When we call C functions, we are entering the territory of Unsafe Rust, where we must manually ensure that pointers are valid and memory is managed correctly.

Declaring an External Function

To call a C function, you must first declare its signature within an extern "C" block. This tells the Rust compiler that the function exists elsewhere and should be linked during the final build process.

Imagine you have a C function called abs that calculates the absolute value of an integer. You would declare it in Rust like this:

extern "C" {
    fn abs(input: i32) -> i32;
}

Using the Function

Because calling this function involves crossing the boundary into C—where the Rust compiler can no longer guarantee safety—you must wrap the call in an unsafe block. This block acts as a formal acknowledgment that you are taking responsibility for the memory safety of the operation.

fn main() {
    let number = -42;
    
    // We use an unsafe block to call the C function
    unsafe {
        let result = abs(number);
        println!("The absolute value of {} is {}", number, result);
    }
}

Important Considerations

When calling C code, you are responsible for maintaining the "contract" expected by the C library. For example, if a C function expects a non-null pointer, passing null from Rust will likely cause a crash. Since C does not have Rust's borrow checker, if you pass a reference to C, you must ensure that the data lives long enough to survive the C function's execution. Always verify that your inputs align exactly with the C function's expectations to prevent undefined behavior.

Key Takeaway: Calling C code requires using an extern "C" block to declare the signature and an unsafe block to execute the function, signaling that you have manually verified the safety of the interaction.