Foreign Function Interface (FFI)
Learn to call C libraries from Rust and expose Rust functions to other languages using FFI for interoperability.
Foreign Function Interface (FFI) is a free Learn Rust Coding lesson on CoddyKit — lesson 1 of 3. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Learn Rust Coding learning path, one of 3 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What is FFI?
FFI stands for Foreign Function Interface. It's a way for programs written in one programming language to call functions or use data structures defined in another language.
In Rust, FFI lets you interact with code written in other languages, most commonly C. This is super useful for integrating with existing libraries or system APIs.
Why Bother with FFI?
FFI isn't an everyday tool, but it's powerful when you need:
- Leverage existing C libraries: Many high-performance, mature libraries are written in C/C++. FFI lets Rust use them directly.
- System APIs: Interact with operating system features that might only have C interfaces.
- Interoperability: Build a Rust library that can be called from other languages (like Python, Java, or JavaScript via Node.js).
Calling C from Rust: Declaration
To call a C function from Rust, you first need to declare its signature using an extern "C" block. This tells Rust what the C function looks like without providing its implementation.
The "C" part specifies the C calling convention, ensuring function calls are compatible.
extern "C" {
// Declare C functions here
// Example: int puts(const char *s);
fn puts(s: *const std::os::raw::c_char) -> std::os::raw::c_int;
// Example: int add(int a, int b);
fn add(a: std::os::raw::c_int, b: std::os::raw::c_int) -> std::os::raw::c_int;
}Calling C from Rust: The `unsafe` Block
Rust's core promise is memory safety. However, when you interact with C code via FFI, Rust cannot guarantee the safety of the foreign code.
Therefore, any call to a C function declared in an extern "C" block must be wrapped in an unsafe block. This signals to the compiler (and other developers) that you are responsible for upholding safety guarantees.
extern "C" {
// We're declaring that a C function `puts` exists.
fn puts(s: *const std::os::raw::c_char) -> std::os::raw::c_int;
}
fn main() {
let rust_string = "Hello from Rust via C!";
// C strings need to be null-terminated.
let c_string = std::ffi::CString::new(rust_string).expect("CString failed");
unsafe {
// Calling the C 'puts' function. This requires 'unsafe'.
puts(c_string.as_ptr());
}
println!("C function call demonstrated.");
}Type Mapping: Rust to C
When using FFI, you must ensure that Rust types correctly map to C types. Rust provides specific types in std::os::raw and std::ffi for this purpose.
i32->std::os::raw::c_intf32->std::os::raw::c_floatbool->std::os::raw::c_uchar(or similar, C lacks a standard boolean)&str->*const std::os::raw::c_char(C strings need null termination, often handled bystd::ffi::CString)&mut [T]->*mut T
Always use these explicit C types to avoid undefined behavior.
Example: Calling a C `add` function
Imagine we have a C library with an int add(int a, int b); function. Here's how we'd call it from Rust. Note the use of c_int for integer types.
(This code demonstrates FFI syntax but won't run without a compiled C library linked externally.)
extern "C" {
// Declaration of the C function `add`
fn add(a: std::os::raw::c_int, b: std::os::raw::c_int) -> std::os::raw::c_int;
}
fn main() {
let x = 10;
let y = 20;
let result = unsafe {
// Call the C 'add' function within an unsafe block
add(x as std::os::raw::c_int, y as std::os::raw::c_int)
};
println!("Attempted to call C add. Result (if linked): {}", result);
}Exposing Rust to C: The Basics
To make a Rust function callable from C, you need two things:
#[no_mangle]: This attribute prevents Rust's compiler from "mangling" (changing) the function's name. C compilers expect simple, unmangled names.extern "C": This specifies that the function should use the C calling convention, making it compatible with C.
Example: Rust Function for C
Here's a Rust function that calculates a sum and could be called from a C program. Notice the #[no_mangle] and extern "C" attributes.
(This code is designed to be compiled as a library for C, but includes a main for internal testing within this environment.)
#[no_mangle]
pub extern "C" fn rust_add_and_print(
a: std::os::raw::c_int,
b: std::os::raw::c_int
) -> std::os::raw::c_int {
let sum = a + b;
println!("Rust function received: {} + {} = {}", a, b, sum);
sum
}
fn main() {
println!("This Rust code exposes a function for C.");
// We can also call it internally for demonstration
let result = rust_add_and_print(5, 7);
println!("Internal call result: {}", result);
}FFI Memory Management
Memory management across FFI boundaries is critical. If Rust allocates memory (e.g., a string) and passes a pointer to C, C must either copy the data or be responsible for freeing it correctly.
Conversely, if C allocates memory and passes a pointer to Rust, Rust should not free it unless explicitly told to, or it must copy the data.
Tools like std::ffi::CString and CStr help manage C-compatible strings safely by handling null termination and ensuring proper ownership transfer.
FFI Quick Check
Let's test your understanding of Rust's Foreign Function Interface.
FFI: Bridge Between Worlds
You've learned about Rust's Foreign Function Interface!
FFI allows Rust to be a true interoperability powerhouse, letting you:
- Call functions from C libraries using
extern "C"andunsafeblocks. - Expose Rust functions to other languages using
#[no_mangle]andpub extern "C". - Handle type mapping carefully with
std::os::rawtypes.
FFI is a powerful tool for integrating Rust into diverse ecosystems, but remember the importance of careful usage, especially regarding safety and memory management!
Frequently asked questions
Is the “Foreign Function Interface (FFI)” lesson free?
Yes — the full text of “Foreign Function Interface (FFI)” is free to read here on the web, and the Learn Rust Coding course includes 3 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Learn Rust Coding course, upgrade to CoddyKit PRO.
What will I learn in “Foreign Function Interface (FFI)”?
Learn to call C libraries from Rust and expose Rust functions to other languages using FFI for interoperability. You practise Learn Rust Coding with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Learn Rust Coding?
No prior experience is required. Learn Rust Coding on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 3, so you can start here or from the beginning and move at your own pace.
How long does the “Foreign Function Interface (FFI)” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Learn Rust Coding lesson?
Yes. Every Learn Rust Coding lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Foreign Function Interface (FFI)
- Rust to WebAssembly (WASM)
- Benchmarking and Performance Tuning