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

Hand a Rust object to C as an opaque handle

Use when: C code must create, use and destroy an object whose type is a Rust struct

Goal: C holds only a pointer, every operation goes through Rust, and the object is freed exactly once by Rust's allocator

Taught in Ownership across the boundary

  1. 1

    Export new, which builds the object in a Box and returns Box::into_raw(b).

    Example

    Box::into_raw(Box::new(Playlist { tracks: Vec::new() }))

    Why: into_raw gives up ownership without freeing, so the object outlives the call and C holds the duty to return it.

  2. 2

    In the header, write only typedef struct Name Name; and pointer-taking prototypes.

    Example

    typedef struct Playlist Playlist;

    Why: C can hold and pass the pointer but can't read or copy the object, so its layout stays Rust's business (no repr(C) needed).

  3. 3

    Export unsafe extern "C" fns that take the handle, turn it into &mut *handle or &*handle, and never keep C's pointers.

    Example

    playlist_add(list, title) copies title into a String.

    Why: only Rust interprets the object, and copying C's data in keeps the object free of pointers into C's memory.

  4. 4

    Export free, which ignores null and otherwise drops Box::from_raw(handle); document that the handle must not be used afterwards.

    Example

    drop(unsafe { Box::from_raw(list) });

    Why: from_raw restores the Box, and dropping it frees the object with the allocator that made it, and only once.

Common mistakes

  • Letting C call free() on the handle: wrong allocator, and the object's own heap data (the Vec, the Strings) would leak.
  • Forgetting the free function entirely, so every handle leaks.
  • Calling Box::from_raw in any function other than the free function: that drop would free the object while C still holds the handle.