The libc crate supplies C-compatible types and platform-specific definitions for Rust foreign function interfaces. Use it when declaring C function signatures or data structures; mismatched types can cause undefined behavior. section 2.6 section 4.8
A reliable C interop boundary follows these practices:
- Declare external functions inside an extern "C" block and link the required native library with #[link(name = "...")]. section 2.6
- Use libc types such as c_int, c_ulong, c_char, c_uint, and c_uchar instead of assuming equivalent Rust types have the same C representation. section 2.6 section 4.8
- Mark mapped structs with #[repr(C)], preserve pointer fields, and keep direct C calls inside unsafe blocks. section 2.6 section 4.8
- Prefer rust-bindgen for substantial headers; manually copy definitions, convert types, and implement interfaces only for small cases. section 2.6 section 4.8
Listing 2.8 Code for zlib utility functions
use libc::{c_int, c_ulong};
#[link(name = "z")]
extern "C" {
fn compress(
dest: *mut u8,
dest_len: *mut c_ulong,
source: *const u8,
source_len: c_ulong,
) -> c_int;
fn compressBound(source_len: c_ulong) -> c_ulong;
fn uncompress(
dest: *mut u8,
dest_len: *mut c_ulong,
source: *const u8,
source_len: c_ulong,
) -> c_int;
}
Shows a compact zlib declaration using libc types, extern "C", and an explicit native-library link. section 2.6
Wrapping zlib
A Rust wrapper allocates a destination Vec, calls compress through an unsafe FFI boundary, sets the returned length, and exposes a safe zlib_compress function. The matching uncompress wrapper can then round-trip compressed bytes. section 2.6
Building a small C library
Cargo can compile src/hello_world.c through the cc build dependency in build.rs, declare the function with libc::c_char, and convert its returned pointer with CStr. Use cargo:rerun-if-changed to avoid unnecessary rebuilds. section 2.11


