About

FFI — Calling C Libraries from Rust

Practical use of extern "C", bindgen, and unsafe

No matter how large the Rust ecosystem grows, it can't replace all existing C/C++ libraries. SQLite, OpenSSL, libcurl — these need direct calling from Rust.

extern "C" {
    fn strlen(s: *const c_char) -> usize;
}
let len = unsafe { strlen(c_str.as_ptr()) };

unsafe is needed because C functions don't follow Rust's ownership/borrowing rules. Pass a null pointer and you get a segfault.

bindgen auto-generates Rust bindings from C header files (.h).

Reverse direction (calling Rust from C): #[no_mangle] pub extern "C" fn — exactly how VOICEVOX Core exposes its C ABI to Python Engine.

Key Points

1

Declare C function signatures in extern "C" block

2

Link C library in build.rs (println!("cargo:rustc-link-lib=..."))

3

Call C functions in unsafe blocks — programmer guarantees safety

4

Wrap in safe Rust wrapper functions to provide safe API to users

Pros

  • ✓ Access to entire C/C++ ecosystem's libraries from Rust
  • ✓ Wrapping unsafe in wrappers keeps the rest safe

Cons

  • ✗ Safety of unsafe code is programmer responsibility
  • ✗ C library build configuration is complex per platform

Use Cases

rusqlite — Rust bindings for SQLite C library VOICEVOX Core — expose Rust as C ABI, call from Python via ctypes
← Rust → WASM — Running Rust in the Browser Variables & Immutability — Why Ruby's Free Reassignment Disappears →