ProgrammingSystem Developer

Explain how the system FFI (Foreign Function Interface) works in Rust. What are the requirements and pitfalls for safely calling functions from C?

Pass interviews with Hintsage AI assistant

Answer

FFI in Rust allows you to call functions declared in external libraries (e.g., C/C++) and export Rust functions for external use. This is done using the extern keyword. Requirements:

  • All FFI calls must be wrapped in an unsafe declaration;
  • The ABI (Application Binary Interface) must match (typically "C");
  • Data types must be compatible — it is important to use primitives with fixed sizes (i32, u64, etc.);
  • Memory management — be very careful with ownership, leaks, and double frees.

Example wrapper:

extern "C" { fn abs(input: i32) -> i32; } fn main() { unsafe { println!("{}", abs(-5)); } }

You can export a function from Rust for use in C like this:

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

Trick question

Question: Does Rust guarantee that if you wrap a C function call in unsafe, everything will work correctly in terms of thread safety and handling UB?

Answer: No! unsafe is a promise to the compiler that you are vouching for the correctness of all safety requirements (aliasing, thread safety, memory access). Rust does not check the code inside C. For example, race conditions or UB in the library code can "break" the runtime of the Rust program. Even if Rust code compiles, actual execution can lead to crashes.


History

In a financial data provider, the team integrated a C library via FFI without checking type sizes. On x86_64, it was assumed that long and i64 matched, but it turned out that sizes didn’t match on other platforms — incorrect memory reads led to bugs in calculations.

History

A developer created a wrapper for the C API, forgetting about the pointers passed to buffers. Since Rust automatically freed memory, the C function sometimes worked with already freed memory, leading to crashes and failures.

History

In a client-server Rust product, a module accessed a C library without considering multi-thread safety. The library was not thread-safe, and Rust programs accessed it from different threads simultaneously, resulting in crashes and data corruption.