πŸ”—

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