Skip to content
Free public beta until December 4, 2026: every course is open, no card needed. After that, the first chapter of each course stays free and full access is a one-time $39 ($19 for up to 100 founding members chosen for helpful feedback). Your progress is kept. How the beta works

Recipe

Compile a C file into a Rust package

Use when: a Rust package carries C source files that must be compiled and linked into it

Goal: cargo build compiles the C code, links it, and reruns the C step only when a C file changes

Taught in Build integration

  1. 1

    Put headers in include/ and C sources in c/, next to Cargo.toml.

    Example

    include/isbn.h, c/isbn.c.

    Why: a fixed layout keeps the build script short and the C code easy to find.

  2. 2

    Under [build-dependencies], add cc = "1".

    Example

    [build-dependencies] then cc = "1".

    Why: only the build script uses cc; nothing of it belongs in the program.

  3. 3

    Print cargo::rerun-if-changed= for each C file and header, then call cc::Build::new().file(..).include(..).compile(name).

    Example

    cc::Build::new().file("c/isbn.c").include("include").compile("isbn");

    Why: cc compiles and archives the code and prints the link lines; the rerun lines stop needless rebuilds.

  4. 4

    Declare the C functions in an unsafe extern "C" block and call each from one safe Rust function.

    Example

    fn isbn10_valid(code: &CStr) -> Option<bool>

    Why: the linker resolves the names from the library cc built; the wrapper keeps unsafe code in one place.

Common mistakes

  • Putting cc under [dependencies]: it then compiles into the program for nothing, and the build script can't use it.
  • Forgetting rerun-if-changed for a header, so edits to it don't trigger a rebuild.
  • Also printing cargo::rustc-link-lib by hand for the same library: cc already does it.