Recipe
Declare and call a C function from Rust
Use when: a Rust program needs a function from a C library, and you have its prototype from a header or its documentation
Goal: a correct declaration, called from one small safe Rust function
Taught in Calling C from Rust
-
1
Find the function's prototype in the C header, such as
size_t strlen(const char *s);.Example
double hypot(double x, double y);Why: the header is the only source of truth for the ABI, and Rust will believe whatever you write.
-
2
Replace every C type with its Rust match:
c_int,c_double,usizeforsize_t,*const c_charforconst char *.Example
fn hypot(x: c_double, y: c_double) -> c_double;insideunsafe extern "C" { }.Why: only FFI-safe types have the size and layout C expects; the improper_ctypes lint catches the obvious misses.
-
3
Mark it
safe fnonly if every possible argument value is valid input; otherwise leave it unsafe.Example
safe fn hypot(...), but plainfn strlen(...), because a pointer can be bad.Why: a
safe fncan be called from safe code, so it must not be able to cause undefined behavior. -
4
Write a safe Rust function whose parameter types guarantee the C function's requirements, with a
// SAFETY:comment on theunsafeblock.Example
fn c_len(s: &CStr) -> usize { unsafe { strlen(s.as_ptr()) } }Why: the rest of the program then calls safe Rust, and the unsafe reasoning lives in one place.
Common mistakes
- Guessing a type (
i64forlong,&strforconst char *) instead of copying it from the header. - Marking a function
safe fnbecause it seemed harmless for the arguments you had in mind. - Forgetting that the declaration doesn't bring in the library: a function outside the C library needs
#[link]or a build script.