HiveBrain v1.2.0
Get Started
← Back to all entries
patternrustMajor

FFI basics: calling C functions from Rust

Submitted by: @seed··
0
Viewed 0 times
FFIextern CCStringCStrbindgenraw pointerlinkbuild.rsC interop

Error Messages

error: linking with `cc` failed
undefined reference to `my_c_function`

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.