Fn, FnMut, FnOnceCrubit supports exporting Rust functions that take closures (Fn, FnMut, FnOnce) to C++ via cpp_api_from_rust.
In C++, callable parameters are represented using rs::FnRef (for borrowed callbacks) and rs::Fn (for owned callbacks). C++ callers can satisfy these parameters using C++ lambdas, function pointers, std::function, or absl::AnyInvocable.
For the reverse direction (calling C++ APIs taking absl::AnyInvocable from Rust), see Rust bindings for C++ callbacks.
BUILD configurationWhen generating C++ bindings for a Rust library via cpp_api_from_rust:
rust_library( name = "rust_api", srcs = ["rust_api.rs"], ) cpp_api_from_rust( name = "rust_api_cc", crate = ":rust_api", ) cc_test( name = "user_of_rust_api", srcs = ["user_of_rust_api.cc"], deps = [ ":rust_api_cc", "//testing/base/public:gunit_main", ], )
The complete, tested version is in examples/types/callbacks/BUILD.
Rust functions can take callable parameters as borrowed references, owned boxes, or generic impl Trait parameters:
| Rust parameter type | Generated C++ parameter type | Ownership / Allocation |
|---|---|---|
&dyn Fn(Args...) -> R | rs::FnRef<R(Args...) const> | Borrowed (no allocation) |
&mut dyn FnMut(Args...) -> R | rs::FnRef<R(Args...)> | Borrowed (no allocation) |
impl Fn(Args...) -> R | rs::FnRef<R(Args...) const> | Borrowed (no allocation) |
impl FnMut(Args...) -> R | rs::FnRef<R(Args...)> | Borrowed (no allocation) |
impl FnOnce(Args...) -> R (without 'static) | rs::FnRef<R(Args...)> | Borrowed (no allocation) |
Box<dyn Fn(Args...) -> R> | rs::Fn<R(Args...) const> | Owning (heap-allocated) |
Box<dyn FnMut(Args...) -> R> | rs::Fn<R(Args...)> | Owning (heap-allocated) |
Box<dyn FnOnce(Args...) -> R> | rs::Fn<R(Args...) &&> | Owning (heap-allocated) |
impl FnOnce(Args...) -> R + 'static | rs::Fn<R(Args...) &&> | Owning (heap-allocated) |
The generated headers include the necessary support headers automatically:
rs::FnRef: #include "support/rs_std/fn_ref.h"rs::Fn: #include "support/rs_std/fn.h"Given a Rust library defining functions that accept closures:
// In rust_api.rs: // Borrowed reference: zero allocation pub fn apply_fn(f: impl Fn(i32) -> i32, x: i32) -> i32 { f(x) } // Mutable borrowed reference: zero allocation pub fn apply_fn_mut(mut f: impl FnMut(i32) -> i32, x: i32) -> i32 { f(x) } // Owned callback: invoked once pub fn apply_fn_once(f: Box<dyn FnOnce(i32) -> i32>, x: i32) -> i32 { f(x) }
C++ callers can pass C++ lambdas, function pointers, std::function, or absl::AnyInvocable:
#include "examples/types/callbacks/rust_api.h" // 1. Pass a C++ lambda to an `impl Fn` parameter: int32_t res1 = rust_api::apply_fn([](int32_t x) { return x * 2; }, 21); // res1 == 42 // 2. Pass an absl::AnyInvocable: absl::AnyInvocable<int32_t(int32_t) const> cb = [](int32_t x) { return x * 3; }; int32_t res2 = rust_api::apply_fn(cb, 14); // res2 == 42 // 3. Pass a mutable lambda to `impl FnMut`: int32_t total = 0; auto mutable_lambda = [&total](int32_t x) mutable { total += x; return total; }; rust_api::apply_fn_mut(mutable_lambda, 10); rust_api::apply_fn_mut(mutable_lambda, 32); // total == 42 // 4. Pass a move-only lambda to `Box<dyn FnOnce>`: auto ptr = std::make_unique<int32_t>(10); int32_t res3 = rust_api::apply_fn_once( [p = std::move(ptr)](int32_t x) { return x + *p; }, 32); // res3 == 42
rs::FnRef vs. rs::Fnrs::FnRef (borrowed): Non-owning view of a callable, similar to absl::FunctionRef. It stores a pointer to the callable and an invoker trampoline. It performs no heap allocation and must not outlive the function call.rs::Fn (owned): Owning callable wrapper. It heap-allocates the provided callable and manages its destruction, making it suitable for storing in Rust structs or transferring ownership across threads. rs::Fn<Sig &&> indicates a callable that can only be invoked once; call it using std::move(f)(...).absl::AnyInvocable (rs::DynCallable)When C++ code receives an owned Rust callback (for example, stored in a Rust struct field exposed to C++), the C++ type is rs::DynCallable<Sig> (support/rs_std/dyn_callable.h).
rs::DynCallable is a move-only functional wrapper around Rust's Box<dyn Trait>. It is implicitly convertible to absl::AnyInvocable:
rs::DynCallable<void() &&> rust_callback = ...; absl::AnyInvocable<void() &&> any_invocable = std::move(rust_callback);
Because rs::DynCallable uses an internal representation compatible with absl::AnyInvocable, this conversion is efficient and avoids re-wrapping or additional heap allocations.