| # `rust_api_from_cpp` | |
| ## Introduction | |
| `rust_api_from_cpp` (aka `rs_bindings_from_cc`) is a Crubit tool that takes | |
| C++ headers as input and generates Rust APIs (a Rust crate) as output, | |
| enabling Rust to call C++. | |
| This document provides instructions for using `rust_api_from_cpp` in Chromium. | |
| See [`//docs/rust/crubit/README.md`](./README.md) for general information about | |
| Crubit, including Crubit availability in projects other than Chromium. | |
| [TOC] | |
| ## Availability | |
| `rust_api_from_cpp` support is currently considered experimental and unstable. | |
| The `rust_api_from_cpp` GN template can only be used when the | |
| `enable_rust_api_from_cpp` GN arg is `true`. It defaults to `false` (see | |
| `//build/config/rust.gni`), but can be set to `true` in `args.gn` for local | |
| experimentation. | |
| TODO(https://crbug.com/40226863): Edit this section once we officially declare | |
| and announce support for using `rust_api_from_cpp`. | |
| ## Generating Rust bindings for a C++ target | |
| Use the `rust_api_from_cpp` template to generate Rust bindings for the public | |
| headers of a C++ target. | |
| Example: | |
| ```cpp | |
| // build/rust/tests/test_rust_api_from_cpp/self_contained_target_header2.h: | |
| namespace test_ns { | |
| int AddViaCc(int x, int y); | |
| struct CcPodStruct final { | |
| int value; | |
| }; | |
| } // namespace test_ns | |
| ``` | |
| ```gn | |
| # build/rust/tests/test_rust_api_from_cpp/BUILD.gn | |
| import("//build/rust/rust_api_from_cpp.gni") | |
| import("//build/rust/rust_static_library.gni") | |
| source_set("self_contained_target") { | |
| sources = [ | |
| "self_contained_target_header2.cc", | |
| "self_contained_target_header2.h", | |
| ] | |
| } | |
| rust_api_from_cpp("self_contained_target_rs_api") { | |
| # This is the C++ target that the bindings are for / call into. | |
| bindings_target = ":self_contained_target" | |
| # Public headers from `sources` of `self_contained_target`. | |
| public_headers = [ | |
| "self_contained_target_header2.h", | |
| ] | |
| # Re-exports items of the `test_ns` C++ namespace at the root of the crate. | |
| root_namespaces = [ "test_ns" ] | |
| } | |
| rust_static_library("rust_api_from_cpp_unittests") { | |
| crate_root = "tests.rs" | |
| sources = [ crate_root ] | |
| deps = [ ":self_contained_target_rs_api" ] | |
| } | |
| ``` | |
| ```rust | |
| // build/rust/tests/test_rust_api_from_cpp/tests.rs: | |
| chromium::import! { | |
| "//build/rust/tests/test_rust_api_from_cpp:self_contained_target_rs_api"; | |
| } | |
| fn foo() { | |
| let sum = ::self_contained_target_rs_api::AddViaCc(100, 42); | |
| let x = ::self_contained_target_rs_api::CcPodStruct { value: 123 }; | |
| } | |
| ``` | |
| Notes: | |
| * By convention, a `rust_api_from_cpp` target is named after its | |
| `bindings_target` with an `_rs_api` suffix (e.g. `foo_rs_api` for `foo`). | |
| * `public_headers` lists only the headers that provide the public API of the | |
| `bindings_target`. Rust bindings are generated only for these headers. | |
| * `root_namespaces` is optional. Without it, Rust code has to write | |
| `self_contained_target_rs_api::test_ns::AddViaCc` (see the "Shorter paths for | |
| items from C++ namespaces" section below). | |
| ## Inspecting the generated bindings | |
| Let's assume that `rust_api_from_cpp` bindings are generated by | |
| `//some/dir:foo_rs_api`. The generated bindings can then be found and | |
| inspected in `<out_dir>/gen/some/dir/foo_rs_api.rs`. For example: | |
| ```sh | |
| $ head -3 out/rel/gen/build/rust/tests/test_rust_api_from_cpp/self_contained_target_rs_api.rs | |
| // Automatically @generated Rust bindings for the following C++ target: | |
| // //build/rust/tests/test_rust_api_from_cpp:self_contained_target_rs_api | |
| // Features: supported, types | |
| ``` | |
| ## Specifying binding dependencies | |
| If public APIs from `public_headers` use types from another C++ target, then | |
| the bindings for that other target need to be listed in the `bindings_deps` | |
| attribute of `rust_api_from_cpp`. | |
| Example (`CreateCcPodStructFromValue` below returns `CcPodStruct` from the | |
| `self_contained_target` used in the earlier examples): | |
| ```cpp | |
| // build/rust/tests/test_rust_api_from_cpp/target_depending_on_another.h: | |
| #include "build/rust/tests/test_rust_api_from_cpp/self_contained_target_header2.h" | |
| namespace test_ns { | |
| inline CcPodStruct CreateCcPodStructFromValue(int x) { | |
| return CcPodStruct{.value = x}; | |
| } | |
| } // namespace test_ns | |
| ``` | |
| ```gn | |
| # build/rust/tests/test_rust_api_from_cpp/BUILD.gn | |
| source_set("target_depending_on_another") { | |
| sources = [ "target_depending_on_another.h" ] | |
| public_deps = [ ":self_contained_target" ] | |
| } | |
| rust_api_from_cpp("target_depending_on_another_rs_api") { | |
| bindings_target = ":target_depending_on_another" | |
| public_headers = [ "target_depending_on_another.h" ] | |
| # Parallels `public_deps` of `target_depending_on_another`. | |
| bindings_deps = [ ":self_contained_target_rs_api" ] | |
| root_namespaces = [ "test_ns" ] | |
| } | |
| rust_static_library("rust_api_from_cpp_unittests") { | |
| crate_root = "tests.rs" | |
| sources = [ crate_root ] | |
| deps = [ | |
| ":self_contained_target_rs_api", | |
| ":target_depending_on_another_rs_api", | |
| ] | |
| } | |
| ``` | |
| ```rust | |
| // build/rust/tests/test_rust_api_from_cpp/tests.rs: | |
| chromium::import! { | |
| "//build/rust/tests/test_rust_api_from_cpp:target_depending_on_another_rs_api"; | |
| } | |
| fn foo() { | |
| // `x` is a `::self_contained_target_rs_api::CcPodStruct`. | |
| let x = ::target_depending_on_another_rs_api::CreateCcPodStructFromValue(456); | |
| assert_eq!(x.value, 456); | |
| } | |
| ``` | |
| Notes: | |
| * `bindings_deps` of `rust_api_from_cpp` list other `rust_api_from_cpp` targets | |
| (e.g. `self_contained_target_rs_api` above) - not the C++ targets (e.g. not | |
| `self_contained_target`). | |
| * `bindings_deps` only need to cover the C++ targets whose types appear in the | |
| public APIs from `public_headers`. Other `deps` of the `bindings_target` | |
| (e.g. ones used only by its `.cc` files) do not need Rust bindings. | |
| ### Types from Rust crates | |
| If public APIs from `public_headers` use Rust types (from a `.h` file generated | |
| by [`cpp_api_from_rust`](cpp_api_from_rust.md)), then `bindings_deps` should | |
| list the `cpp_api_from_rust` target. The Rust bindings will then refer to the | |
| original Rust types. See `cpp_lib_using_rust_type_rs_api` in | |
| `build/rust/tests/test_rust_api_from_cpp/round_trip/BUILD.gn` for an example. | |
| ## References and lifetimes | |
| Crubit binds C++ references (including `this`) as Rust references (or as | |
| `CRef` / `CMut`). By default, the lifetimes of these references come from | |
| Rust's lifetime elision rules. These rules can be wrong - for example, when a | |
| returned reference can point into a parameter other than `this`. In such | |
| cases, annotate the C++ API: | |
| * Prefer `LIFETIME_BOUND` (from `base/compiler_specific.h`). Crubit translates | |
| it into Rust lifetimes. Clang warnings also lightly check it (e.g. | |
| `-Wdangling` reports a temporary passed to a `LIFETIME_BOUND` parameter | |
| if the result outlives the temporary). See | |
| `build/rust/tests/test_rust_api_from_cpp/lifetimes/lifetime_bound.h`. | |
| * Use explicit lifetime annotations (e.g. `$a` from | |
| `#include "third_party/crubit/support/lifetime_annotations.h"`) only if | |
| `LIFETIME_BOUND` can't express the lifetimes. Nothing checks that these | |
| annotations are correct. C++ targets that use these annotations need to | |
| depend on `//build/rust/crubit`. See | |
| `build/rust/tests/test_rust_api_from_cpp/lifetimes/explicit_lifetimes.h`. | |
| See | |
| [Crubit's "References and lifetimes" docs](https://crubit.rs/cpp/references_and_lifetimes.html) | |
| for more details and examples. | |
| See also https://crbug.com/572054453 and https://crbug.com/572056816 which track | |
| enabling additional lifetime-related warnings in Chromium's C++ targets. | |
| ## C++ standard library types | |
| Some C++ standard library types (e.g. `std::optional<T>`, `std::unique_ptr<T>`, | |
| `std::vector<T>`, and `std::string_view`) are represented in the generated | |
| bindings by layout-compatible Rust types from the `cpp_std` crate. To name | |
| these types, Rust code should depend on `//build/rust/crubit:cpp_std` and write | |
| e.g. `cpp_std::vector<T>`. | |
| See `build/rust/tests/test_rust_api_from_cpp/std/std_types.h` and | |
| `build/rust/tests/test_rust_api_from_cpp/std/unittests.rs`. | |
| `std::string_view` is represented as `cpp_std::string_view<'a>` (see the | |
| "References and lifetimes" section above). | |
| ## Types that Rust can't move | |
| Rust moves values with `memcpy` - it cannot move C++ objects that are not Rust | |
| movable. For more details please see the following sections of the Crubit book: | |
| * ["Rust-movable classes"](https://crubit.rs/cpp/classes_and_structs.html#rust_movable) | |
| (generic introduction) | |
| * ["Making types Rust-movable"](https://crubit.rs/cpp/cookbook#rust_movable) | |
| (tips for making a C++ object Rust-movable). Some extra notes about | |
| `TRIVIAL_ABI`: | |
| - Note that in Chromium `TRIVIAL_ABI` from `base/compiler_specific.h` | |
| should be used instead of the equivalent Abseil macro. | |
| - Note that Crubit docs and | |
| [Clang docs](https://clang.llvm.org/docs/AttributeReference.html#trivial-abi:~:text=objects%20that%20contain%20pointers%20to%20themselves%20or%20otherwise%20depend%20on%20their%20address%20(or%20the%20address%20or%20their%20subobjects) | |
| point out that misusing `TRIVIAL_ABI` can lead to | |
| Undefined Behavior (UB) - e.g. when the attribute is applied to | |
| objects that are not | |
| [bitwise-relocatable (as defined by Crubit)](https://github.com/google/crubit/blob/4fd627869b9e9becbf4c041f7a511102c2db4ce1/docs/design/bitwise_relocatable.md?plain=1#L35-L52). | |
| * `ctor` crate documentation (support for in-place construction and mutation of | |
| Rust types like ones that were originally defined in C++): | |
| - ["Quick guide"](https://crubit.rs/types/non_rust_movable/intro_short.html) | |
| - ["Advanced guide"](https://crubit.rs/types/non_rust_movable/intro_advanced.html) | |
| - ["Cheat sheet"](https://crubit.rs/types/non_rust_movable/cheat_sheet.html) | |
| ## Adding hand-written Rust code to the bindings | |
| The `extra_rust_sources` attribute of `rust_api_from_cpp` merges hand-written | |
| `.rs` files into the generated bindings crate. This is mostly useful for | |
| implementing traits (e.g. `TryFrom`, `Display`, `Iterator`) for the types from | |
| the generated bindings. Rust's | |
| [orphan rule](https://doc.rust-lang.org/reference/items/implementations.html#r-items.impl.trait.orphan-rule) | |
| prevents such trait impls in other crates (see also the "Troubleshooting" | |
| section below). | |
| Example: | |
| ```cpp | |
| // build/rust/tests/test_rust_api_from_cpp/extra_rust_sources/time_delta.h: | |
| namespace test_base { | |
| class TestTimeDelta { | |
| public: | |
| static constexpr TestTimeDelta FromMicroseconds(int64_t us); | |
| constexpr int64_t InMicroseconds() const; | |
| ... | |
| }; | |
| } // namespace test_base | |
| ``` | |
| ```rust | |
| // build/rust/tests/test_rust_api_from_cpp/extra_rust_sources/time_delta.rs: | |
| use crate::test_base::TestTimeDelta; | |
| use core::num::TryFromIntError; | |
| use core::time::Duration; | |
| impl TryFrom<TestTimeDelta> for Duration { | |
| type Error = TryFromIntError; | |
| fn try_from(delta: TestTimeDelta) -> Result<Self, Self::Error> { | |
| let us = u64::try_from(delta.InMicroseconds())?; | |
| Ok(Duration::from_micros(us)) | |
| } | |
| } | |
| ``` | |
| ```gn | |
| # build/rust/tests/test_rust_api_from_cpp/extra_rust_sources/BUILD.gn | |
| rust_api_from_cpp("time_delta_rs_api") { | |
| bindings_target = ":time_delta" | |
| public_headers = [ "time_delta.h" ] | |
| root_namespaces = [ "test_base" ] | |
| extra_rust_sources = [ "time_delta.rs" ] | |
| } | |
| ``` | |
| ```rust | |
| // build/rust/tests/test_rust_api_from_cpp/extra_rust_sources/unittests.rs: | |
| chromium::import! { | |
| "//build/rust/tests/test_rust_api_from_cpp/extra_rust_sources:time_delta_rs_api" | |
| as time_delta; | |
| } | |
| fn foo() { | |
| let delta = ::time_delta::TestTimeDelta::FromMicroseconds(123); | |
| let duration = std::time::Duration::try_from(delta).unwrap(); | |
| } | |
| ``` | |
| Notes: | |
| * Each file from `extra_rust_sources` becomes a private module at the root of | |
| the crate. All items of this module are re-exported (via `pub use`) from the | |
| root of the crate. | |
| * Prefer to keep `extra_rust_sources` limited to Rust-specific glue (e.g. trait | |
| impls or conversions to Rust standard library types). If an API would also be | |
| useful to C++ callers, then add it to the C++ header instead - Crubit will | |
| generate Rust bindings for it, and both languages will share one | |
| implementation. | |
| * Injecting Rust sources into a _nested module_ of the generated bindings is | |
| supported via the `extra_rust_source_groups` attribute. Each group lists | |
| `sources` and a Rust module path in `mod` (e.g. `"foo::bar"`) - the sources | |
| are placed into that module. Reaching for this attribute should be | |
| needed quite rarely - usually only for `cc_std` or `base` bindings. See | |
| `source_groups_rs_api` in | |
| [`build/rust/tests/test_rust_api_from_cpp/extra_rust_sources/BUILD.gn`](../../../build/rust/tests/test_rust_api_from_cpp/extra_rust_sources/BUILD.gn) | |
| for an example. | |
| ## Shorter paths for items from C++ namespaces | |
| C++ namespaces become Rust modules in the generated bindings (e.g. | |
| `test_outer::test_inner::GetValueFromNestedNamespace` from C++ becomes | |
| `test_root_namespaces::test_outer::test_inner::GetValueFromNestedNamespace` in | |
| Rust). The `root_namespaces` attribute of `rust_api_from_cpp` also re-exports | |
| the items of the specified namespaces at the root of the crate. | |
| Example: | |
| ```gn | |
| # base/BUILD.gn | |
| rust_api_from_cpp("base_rs_api") { | |
| bindings_target = ":base" | |
| root_namespaces = [ "base" ] | |
| # ... | |
| } | |
| ``` | |
| ## Troubleshooting | |
| The sections below should help diagnose and fix some issues you may encounter. | |
| If your issue is not covered, then please see | |
| [`docs/rust/crubit/README.md`](./README.md) for instructions | |
| how to report a new bug. | |
| ### APIs missing from the generated bindings | |
| If `rust_api_from_cpp` is unable to generate bindings for a given C++ API, | |
| then the generated `.rs` file will contain a comment explaining why. | |
| #### Crubit is not enabled on defining target | |
| If you see an error like: | |
| ``` | |
| $ cat out/rel/gen/build/rust/tests/test_rust_api_from_cpp/target_depending_on_another_rs_api.rs | |
| ... | |
| // error: function `CreateCcPodStructFromValue` could not be bound | |
| // Cannot use an error type `CcPodStruct` by value: | |
| // Crubit is not enabled on defining target: | |
| // ../../build/rust/tests/test_rust_api_from_cpp/self_contained_target_header2.h | |
| ... | |
| ``` | |
| Then you want to read the "Specifying binding dependencies" section above. | |
| ### Can't implement a trait for a type from the generated bindings | |
| Rust's | |
| [orphan rule](https://doc.rust-lang.org/reference/items/implementations.html#r-items.impl.trait.orphan-rule) | |
| prevents implementing a foreign trait for foreign types. For example, if | |
| `extra_rust_sources/unittests.rs` tries to implement `TryFrom` for a type from | |
| `rust_api_from_cpp` bindings, then the build fails with an error like: | |
| ``` | |
| error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate | |
| --> ../../build/rust/tests/test_rust_api_from_cpp/extra_rust_sources/unittests.rs:47:1 | |
| | | |
| 47 | impl TryFrom<::time_delta::test_base::TestTimeDelta> for std::time::Duration { | |
| | ^^^^^-----------------------------------------------^^^^^------------------- | |
| | | | | |
| | | `Duration` is not defined in the current crate | |
| | this is not defined in the current crate because this is a foreign trait | |
| ``` | |
| To fix this, move the trait impl into the generated bindings crate by using | |
| `extra_rust_sources` (see the "Adding hand-written Rust code to the bindings" | |
| section above). | |
| ### Dependency cycles and missing dependencies | |
| `rust_api_from_cpp` parses `public_headers` (and all headers that they | |
| `#include`, directly or transitively) when it generates the bindings. Therefore | |
| GN dependencies need to be declared correctly and completely: all these headers | |
| need to be (transitively) provided by `bindings_target`. | |
| This is stricter than what Chromium needed in the past: | |
| * Many Chromium targets are big and monolithic (e.g. `//chrome/browser` or | |
| `//content/browser`). | |
| * GN does not enforce correct dependencies as strictly as some other build | |
| systems: | |
| * `gn check` ignores generated files by default (see `--check-generated` in | |
| the [GN reference for `gn check`](https://gn.googlesource.com/gn/+/main/docs/reference.md#cmd_check)). | |
| https://crbug.com/558554239 tracks fixing the violations in generated | |
| files (see `no_check_generated_targets` in [`//.gn`](../../../.gn)). | |
| * By default `gn check` allows public headers to `#include` headers from | |
| private `deps`. Targets can opt into stricter checks with | |
| `check_includes_strict = true` (see | |
| [`build_performance.md`](./build_performance.md)). | |
| [`cxx`](https://cxx.rs) does not read C++ headers when it generates its | |
| bindings. The headers are used only later, when the generated `.cc` file is | |
| compiled. Therefore `cxx` bindings may compile even if their dependencies are | |
| missing (e.g. because a generated header was already generated for another | |
| target). This can cause flaky builds. For example, | |
| `no_check_generated_targets` (see above) has several `..._cxx_generated` | |
| entries, because `gn check --check-generated` reports missing dependencies in | |
| the C++ files generated by `cxx`. When you replace `cxx` with | |
| `rust_api_from_cpp`, you may have to add the missing dependencies. | |
| This is mostly a problem when Rust is added in the middle of a big target. For | |
| example, if C++ code in `//content/browser` calls Rust, and this Rust code calls | |
| other C++ APIs from `//content/browser`, then GN reports a dependency cycle. | |
| To fix this, move the C++ APIs that Rust calls into a separate, smaller target | |
| (see also the "Modularity" section in | |
| [`chrome_browser_design_principles.md`](../../chrome_browser_design_principles.md#modularity)). | |
| There is no such problem when Rust only calls C++ APIs from lower layers (e.g. | |
| from `//base`), unless the bindings of these APIs are also used by Rust code | |
| inside the lower layer. | |
| Smaller targets with correct dependencies are good for Chromium even without | |
| Rust. They make API boundaries clearer, and they can make builds faster and | |
| more reliable. | |
| ### Different bindings on different target platforms | |
| Public API of Crubit-generated bindings should be the same on all target | |
| platforms. There are some known issues where the bindings may differ (e.g. see | |
| Google-internal b/564616479), but please do not work around the differences with | |
| something like `#[cfg(windows)]` - please report bugs instead. |