In this lesson, you will learn how to safely bridge the gap between Rust’s strongly typed primitive system and the platform-specific representation of C types used in FFI.
The ABI Mismatch Problem
When interacting with C code, it is insufficient to simply assume that a Rust i32 corresponds to a C int. While they often match, the C standard defines int, long, and size_t as platform-dependent types. For example, a C long might be 32 bits on one system and 64 bits on another, whereas Rust’s i64 is strictly 64 bits. Using the wrong size will lead to undefined behavior or silent data corruption when passing arguments across the FFI boundary.
Using std::os::raw and core::ffi
To maintain memory safety while interfacing with C, you must use the types provided in std::os::raw (or the newer core::ffi module). These types are aliases that automatically resolve to the correct native platform width at compile time.
For instance, if you are defining a function signature for a C library, you should avoid native Rust integers like i32 if the C side expects a platform-specific int. Instead, use c_int:
use std::ffi::c_int;
extern "C" {
// Correctly maps to the C 'int' type on the current platform
fn process_data(value: c_int) -> c_int;
}
fn main() {
let input: c_int = 42;
unsafe {
let result = process_data(input);
println!("Result from C: {}", result);
}
}
By using c_int, c_long, c_char, and c_double, you ensure that the memory layout and register usage align perfectly with the target's Application Binary Interface (ABI). This prevents "type tearing" where Rust and C disagree on the memory footprint of a variable, which is a common source of memory corruption in low-level systems.
Always prefer these explicit FFI types over native Rust primitives when defining external boundaries. This practice ensures your code remains portable across different architectures without manual adjustments.
Key Takeaway: Always use platform-specific FFI types from core::ffi or std::os::raw instead of native Rust primitives to guarantee ABI compliance and prevent memory layout mismatches across the FFI boundary.