Saltar a contenido

Week 14 - FFI: Calling C, Being Called By C

14.1 Conceptual Core

  • The C ABI is the lingua franca. Rust can both consume it (extern "C" fn declarations) and expose it (#[no_mangle] pub extern "C" fn).
  • Two directions of pain:
  • Calling C from Rust-bindings, header parsing (bindgen), null-pointer discipline, CStr/CString lifetimes, errno.
  • Calling Rust from C-symbol mangling, panic discipline (panics across FFI = UB), opaque pointers, version-stable headers (cbindgen).

14.2 Mechanical Detail

  • bindgen: parses C headers via libclang, emits Rust extern declarations. Configure via wrapper.h and build.rs. Common pitfalls: bitfield representation, function-like macros (not handled), inline functions (must be static-libbed separately).
  • cbindgen: the inverse. Reads your Rust source and emits a C header. Requires a cbindgen.toml. Generated headers should be checked into include/ and CI-verified.
  • #[repr(C)] is mandatory for any type crossing the boundary. #[repr(transparent)] for newtype wrappers around FFI primitives.
  • Strings: CStr is unsized borrowed, CString is owned. Never assume a *const c_char is UTF-8-convert through CStr::from_ptr and then to_str().
  • Panic safety across FFI: panics that unwind across an extern "C" boundary are UB. Wrap every extern "C" fn body in std::panic::catch_unwind and convert panics to error codes, or compile with panic = "abort".
  • errno: thread-local in glibc; access via std::io::Error::last_os_error() immediately after a syscall.

14.3 Lab-"Bind a Real C Library and Expose a Rust One"

Two parts: 1. Consume: write Rust bindings to libsodium's crypto_secretbox family. Use bindgen for the raw layer, then wrap in safe Rust (own the keys with Zeroizing<[u8; 32]>, use typed nonces, return Results). 2. Expose: take your parse-units crate from Month 1 and ship a C-callable parse_units_c library with a cbindgen - generated header. Provide aMakefile` that links a tiny C program against it.

14.4 Idiomatic & Clippy Drill

  • clippy::not_unsafe_ptr_arg_deref, clippy::missing_safety_doc, clippy::fn_to_numeric_cast_any, clippy::transmute_ptr_to_ptr. The first two are FFI-specific safety doc enforcers.

14.5 Production Hardening Slice

  • Cross-compile your parse-units C library to aarch64-unknown-linux-gnu and x86_64-unknown-linux-musl. Verify the resulting .so/.a artifacts with nm and readelf. Add this to CI as a matrix build.

Comments