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 asadd. In the 2024 edition,no_mangleis anunsafeattribute, 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 markedsafe fn. "C"is the ABI — how functions are called at the binary level.#[unsafe(no_mangle)] pub extern "C" fnlets C call Rust.- An
externblock can declarestaticvariables too.