patternrustMajor
FFI basics: calling C functions from Rust
Viewed 0 times
FFIextern CCStringCStrbindgenraw pointerlinkbuild.rsC interop
Error Messages
Problem
Integrating with existing C libraries requires understanding how to declare C function signatures, handle C types, and manage memory across the boundary.
Solution
Use extern "C" blocks to declare C functions and link with the correct library:
// Declare C functions (in their C-compatible signature)
extern "C" {
fn abs(x: i32) -> i32;
fn strlen(s: *const std::os::raw::c_char) -> usize;
fn malloc(size: usize) -> *mut std::os::raw::c_void;
fn free(ptr: *mut std::os::raw::c_void);
}
fn main() {
// All calls to C functions are unsafe
let result = unsafe { abs(-42) };
println!("abs(-42) = {}", result);
// Convert Rust str to C string
use std::ffi::CString;
let c_str = CString::new("hello").unwrap();
let len = unsafe { strlen(c_str.as_ptr()) };
println!("strlen = {}", len);
}
// Link a C library in build.rs:
// println!("cargo:rustc-link-lib=mylib");
// println!("cargo:rustc-link-search=native=/usr/local/lib");Why
Rust guarantees a stable C ABI via extern "C" using the platform's C calling convention. Memory allocated by C must be freed by C and vice versa — the ownership model does not cross the FFI boundary.
Gotchas
- CString must stay alive as long as the raw pointer is used — dropping it earlier is a dangling pointer
- Rust strings are not null-terminated — always use CString/CStr for C interop, never pass &str directly
- Use the bindgen tool to auto-generate Rust bindings from C header files rather than writing them manually
Revisions (0)
No revisions yet.