Hello,
Chromium developers are now able to use Crubit to call Rust code from C++.
For those who are unaware, Crubit is a Google-developed tool for automatically generating interop bindings between Rust and C++. In comparison to our current tooling (based on cxx), Crubit supports a wider variety of Rust features, and does not require an explicit declaration which APIs should be exposed to the other language. For a more detailed comparison, see the post-scriptum at the end of this email.
We do not intend to deprecate cxx, but we do expect Crubit to be our primary interop tool once it is fully supported. At the moment there are still restrictions on Crubit’s use in Chromium:
Only the Rust -> C++ direction is supported (generating “cpp_api_from_rust” aka “cc_bindings_from_rs”). We intend to add support for calling C++ code from Rust as soon as possible (this is tracked in https://crbug.com/40226863).
Crubit cannot yet be used in //base, //net, or other directories that Cronet depends on. This is because the Android project’s build system uses Soong/bp which doesn’t yet support Crubit (from Chromium side this is tracked in https://crbug.com/535682335). (Note that Chromium’s GN/ninja builds support Crubit on all target platforms, including Android.)
Similarly to the bullet item above, using Crubit in second-party projects like V8 or PDFium may require ensuring that build systems of all their downstream clients can support Crubit.
Crubit is currently being used in //components/qr_code_generator and will ship to Chromium users in M152. The Rust-in-Chrome team is more than happy to assist anyone who is interested in using Crubit in Chromium, either by migrating existing cxx code, or in service of a new project. For usage instructions and additional links and information, please see //docs/rust/crubit.md in the Chromium repo.
Best regards,
Lukasz Anforowicz (on behalf of the Rust-in-Chrome team)
PS. Below are examples how Crubit resulted in a different FFI experience in the qr_code_generator code:
No manifest
cxx requires declaring the cross-language API surface in a hand-written #[cxx::bridge] mod ffi { … }
Crubit’s cpp_api_from_rust consumes a Rust crate (a set of .rs files) as input, and tries to generate C++ bindings for all public APIs. Unsupported Rust APIs are replaced with C++ comments that describe the error and/or point to the bug tracking a missing Crubit feature.
For example, going forward we can remove components/qr_code_generator/qr_code_generator_ffi_glue.rs - see https://crrev.com/c/8106171.
Passing Rust objects by value
cxx treats foreign types as opaque - non-POD (plain old data) C++ and Rust objects can’t be passed by value across the FFI boundary. Passing such objects requires indirection of a reference or a Box or UniquePtr.
Crubit’s cpp_api_from_rust is built on top of rustc_driver crates, which means that it can understand the exact memory layout of Rust structs and enums. This lets C++ pass and store Rust objects by value.
For example, qr_code_generator can directly store the qr_code::QrCode Rust struct - see qr_code_generator.cc:52.
Support for Option<T>, and Result<T, E> generic types
cxx doesn’t support Option<T>, nor Result<T, E>. (It supports throwing a C++ exception when `Result<T, E>` indicates an error, but this doesn’t work in Chromium where C++ exceptions and Rust panics are not supported and result in an immediate abort.)
Crubit explicitly supports Option<T> and Result<T, E> - see crubit/support/rs_std/option.h and crubit/support/rs_std/result.h. (Crubit support library is unfortunately not covered by Chromium code search because of https://crbug.com/470138209 and b/464078153, but you can find it on GitHub or in your local Chromium repo.)
For example, qr_code_generator can receive Result<T, E> by value, identify the enum variant via has_value(), and then either extract the err() or value() - see qr_code_generator.cc:48-52.
Support for PartialEq and PartialOrd traits
cxx doesn’t support generating operator== bindings for PartialEq. For the other FFI direction it could in theory generate a non-trait method for operator==, but it is not currently supported - see https://github.com/dtolnay/cxx/pull/1689.
Crubit maps impl PartialEq to operator== and impl PartialOrd to operator<=>.
For example, qr_code_generator uses equality comparison of qr_code::types::QrError - see qr_code_generator.cc:34.
Support for Iterator trait
cxx doesn’t support the Iterator trait
Crubit generates bindings for all traits (well, excluding some standard library traits which get more idiomatic bindings). Such bindings are then used to provide a C++ adapter of Rust iterators - see crubit/support/rs_std/iterator_adapter.h. In particular note the C++ equivalent of Rust where clause here: requires(rs_std::where_v<TAdaptedIterator, rs::core::iter::Iterator>)
For example, qr_code_generator is able to use std::ranges::transform to consume values from a Rust iterator - see qr_code_generator.cc:67-71 (the rs::IteratorAdapter and rs::IteratorEnd come from crubit/support libraries).
Support for #[repr(Rust)] enums
cxx doesn’t support passing by value, constructing, or inspecting of Rust enums
Crubit supports constructing tuple enums. (Constructing struct enums is not yet supported and is tracked in b/487357254.)
For example, qr_code_generator can construct
qr_code::types::Version::Normal(i16) variant of a Rust enum - see qr_code_generator.cc:55.
qr_code::types::EcLevel::M variant of a Rust enum - see qr_code_generator.cc:58
Support for some generic functions
cxx doesn’t support generic functions
Crubit doesn’t support arbitrary generic functions, but it can support ones that can successfully apply canonical monomorphizations (e.g. Into<T> becomes T, AsRef<T> becomes &T, etc.).
For example, qr_code_generator is able to call into pub fn new<D: AsRef<[u8]>>(data: D) -> QrResult<Self> - see qr_code_generator.cc:48