absl::AnyInvocableCrubit supports calling C++ functions that accept or return absl::AnyInvocable from Rust.
In Rust, absl::AnyInvocable parameters and return types map to standard Rust trait objects (Box<dyn Fn...> and Box<dyn FnOnce...>).
For the reverse direction (calling Rust functions that take closures from C++), see C++ bindings for Rust callbacks.
BUILD configurationTo generate Rust bindings for a C++ library that uses absl::AnyInvocable, ensure the cc_library depends on @abseil-cpp//absl/functional:any_invocable and is opted into Crubit using rust_api_from_cpp:
cc_library( name = "cpp_api", srcs = ["cpp_api.cc"], hdrs = ["cpp_api.h"], aspect_hints = [":cpp_api_rust.hint"], deps = [ "@abseil-cpp//absl/functional:any_invocable", ], ) rust_api_from_cpp( name = "cpp_api_rust", cpp_target = ":cpp_api", ) rust_test( name = "user_of_cpp_api", srcs = ["user_of_cpp_api.rs"], deps = [ ":cpp_api_rust", "//third_party/gtest_rust/googletest", ], )
The complete, tested version is in examples/types/callbacks/BUILD.
Crubit maps absl::AnyInvocable to heap-allocated Rust trait objects:
C++ absl::AnyInvocable signature | Rust binding type |
|---|---|
absl::AnyInvocable<R(Args...) const> | Box<dyn Fn(Args...) -> R + Send + Sync> |
absl::AnyInvocable<R(Args...) &&> | Box<dyn FnOnce(Args...) -> R + Send> |
absl::AnyInvocable<R(Args...)> (non-const) | Box<dyn Fn(Args...) -> R + Send + Sync> |
absl::AnyInvocable is promoted to FnNon-const absl::AnyInvocable<R(Args...)> is mapped to Box<dyn Fn...> (rather than FnMut) in Rust.
In C++, invoking a non-const absl::AnyInvocable does not require exclusive mutable access to the function object. Rust's FnMut, by contrast, requires an exclusive &mut borrow that would be unsound if called concurrently or re-entrantly from C++. Mapping to Fn guarantees that the callback is safe to invoke in any context.
If you need to mutate state across invocations in a Rust closure passed to a non-const absl::AnyInvocable, use interior mutability types such as Atomic* (e.g. AtomicI32), Cell, RefCell, or Mutex instead of &mut capture.
C++ functions can accept absl::AnyInvocable by value:
// In cpp_api.h: #include <cstdint> #include "third_party/absl/functional/any_invocable.h" // Accepts a callback that may be called multiple times: int32_t ApplyMultiplier(absl::AnyInvocable<int32_t(int32_t) const> callback, int32_t value); // Accepts a single-use callback: void InvokeOnce(absl::AnyInvocable<void() &&> callback);
In Rust, pass a boxed closure (Box::new(...)):
// In user_of_cpp_api.rs: // 1. Calling a const callback: let result = cpp_api::ApplyMultiplier(Box::new(|x: i32| x * 2), 21); assert_eq!(result, 42); // 2. Calling a single-use (FnOnce) callback: let data = String::from("resource"); cpp_api::InvokeOnce(Box::new(move || { println!("Consumed: {data}"); }));
C++ functions can return absl::AnyInvocable:
// In cpp_api.h: absl::AnyInvocable<int32_t(int32_t) const> MakeAdder();
In Rust, the returned value is a callable trait object that can be called directly:
let adder = cpp_api::MakeAdder(); assert_eq!(adder(41), 42);
Type aliases to absl::AnyInvocable in C++ receive corresponding type alias bindings in Rust:
using IntMapper = absl::AnyInvocable<int32_t(int32_t) const>; int32_t ApplyAlias(IntMapper callback, int32_t value);
In Rust, cpp_api::IntMapper is an alias for Box<dyn Fn(i32) -> i32 + Send + Sync>.
std::function is not bound: Crubit does not generate bindings for functions taking or returning std::function. Replace std::function with absl::AnyInvocable in your C++ API, or write a thin C++ wrapper taking absl::AnyInvocable.