In this lesson, you will learn how to bridge the gap between Rust’s expressive closures and the rigid requirement of C-style function pointers using a technique called "trampolining."
The Impedance Mismatch
In C, a callback is typically a raw function pointer void (*callback)(void*). Because C knows nothing about Rust’s Fn traits or captured environment, you cannot pass a Rust closure directly into C code. A closure is a struct containing data and a code pointer; a C function pointer is just a code address.
To resolve this, we use a trampoline: a static, extern "C" function that acts as a middleman.
Implementing the Trampoline
When you need to pass a closure to C, you perform two steps:
- The Context Pointer: Cast the closure reference into a
*mut c_voidpointer to pass as the "user data" argument. - The Trampoline: Create an
extern "C"function that receives that*mut c_void, casts it back into the original closure type, and invokes it.
use std::ffi::c_void;
// The C-style callback function
extern "C" fn trampoline<F>(data: *mut c_void)
where F: Fn() {
// Reconstruct the closure from the pointer
let closure = unsafe { &*(data as *const F) };
closure();
}
fn register_callback<F: Fn()>(f: &F) {
let ptr = f as *const F as *mut c_void;
// Pass both the trampoline and the pointer to C
unsafe { c_api_register(Some(trampoline::<F>), ptr) };
}
Critical Considerations
Because the C code is unaware of Rust's ownership model, the closure must outlive the registration lifetime. If the C library stores this pointer for later execution, you must ensure the closure data resides in a stable memory location (like the heap via Box::into_raw) and is managed manually to avoid dangling pointers.
The trampoline is essentially an "adapter" that strips away the high-level closure semantics, allowing Rust to satisfy the C ABI while retaining the ability to execute the captured environment once control returns to the Rust side.
Key Takeaway: To pass a closure to C, you must pack the closure as a *mut c_void and provide a static extern "C" trampoline function to cast and execute that pointer safely.