Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

extern Blocks

Goal of This Episode

Learn to call C functions and let C call Rust functions. This episode is only a rough tour of FFI. If you want a complete FFI example (from building a C library to calling it from Rust), search for a dedicated tutorial.

Concept

What Is FFI

FFI (Foreign Function Interface) is the mechanism that lets different programming languages call each other’s functions. Rust can call functions written in C, and C can call functions written in Rust. Since nearly every language can interoperate with C, Rust can talk to most languages by using C as the bridge.

Calling C Functions

Declare external C functions with an unsafe extern "C" block:

unsafe extern "C" {
    fn fabs(x: f64) -> f64;
}

fn main() {
    let result = unsafe { fabs(-42.0) };
    println!("fabs(-42.0) = {}", result);
}

Calling an external function requires unsafe — Rust has no way to check whether the function on the C side is safe.

Since the Rust 2024 edition, the extern block itself also requires unsafe — because Rust can’t verify that the function signatures you wrote in the declaration (parameter types, return type, etc.) are correct. If a signature doesn’t match what’s actually on the C side, that’s undefined behavior.

safe fn

If you’re certain an external function is safe, you can mark it safe:

unsafe extern "C" {
    safe fn fabs(x: f64) -> f64; // fabs is safe for every f64 input
}

fn main() {
    let result = fabs(-42.0); // callable without unsafe!
    println!("fabs(-42.0) = {}", result);
}

What the "C" Means

The "C" in extern "C" refers to the ABI (Application Binary Interface) — how functions are called at the binary level. "C" is the most common ABI; nearly every language can interoperate with the C ABI.

Letting C Call Rust

#[unsafe(no_mangle)]
pub extern "C" fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {}
  • extern "C": use the C ABI.
  • #[unsafe(no_mangle)]: don’t mangle the function name, so C can find it as add. In the 2024 edition, no_mangle is an unsafe attribute, because it changes how the function is linked, which can affect safety.

extern Blocks Can Declare static Variables Too

unsafe extern "C" {
    static daylight: i32; // a global variable on the C side
}

fn main() {}

Example Code

unsafe extern "C" {
    safe fn fabs(x: f64) -> f64;
    fn sqrt(x: f64) -> f64;
}

#[unsafe(no_mangle)]
pub extern "C" fn rust_add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    // functions marked safe don't need unsafe
    println!("fabs(-10.0) = {}", fabs(-10.0));

    // unmarked ones do
    let root = unsafe { sqrt(25.0) };
    println!("sqrt(25.0) = {}", root);

    // Rust's extern "C" functions can also be called directly from Rust
    println!("rust_add(3, 4) = {}", rust_add(3, 4));
}

Recap

  • unsafe extern "C" { ... } declares external C functions.
  • Calling external functions requires unsafe, except those marked safe fn.
  • "C" is the ABI — how functions are called at the binary level.
  • #[unsafe(no_mangle)] pub extern "C" fn lets C call Rust.
  • An extern block can declare static variables too.