Rust libraries can be used directly from C++. This page documents roughly what that entails, and additional subpages (available in the left-hand navigation) document specific aspects of the generated bindings.
Tip: The code examples below are pulled straight from https://github.com/google/crubit/tree/main/examples/rust/function/. The other examples in https://github.com/google/crubit/tree/main/examples/rust/ are also useful. If you prefer just copy-pasting something, start there.
Crubit allows you to call some Rust interfaces from C++. It supports functions (including methods), structs, traits, and even enums as “opaque” objects. Crubit does not support advanced features like generics or dynamic dispatch with dyn.
The rest of this document goes over how to create a Rust library that can be called from C++, and how to actually use it from C++. The quick summary is:
All rust_library targets can receive C++ bindings.
To use the bindings for a target //path/to:example_crate, you must create a C++ rule exporting the bindings, using cpp_api_from_rust(name="any_name_here", crate=":example_crate").
The header name is the Rust target's label with a .h appended: to include the header for the Rust library //path/to:example_crate, you use #include "path/to/example_crate.h".
The namespace name is the Rust target name, e.g. example_crate. To change the namespace, use cc_bindings_from_rust_library_config, described below.
To see the generated C++ API, right click the "path/to/example_crate.h" include in Cider, and select “Go to Definition”.
NOTE: In some cases the generated file in Cider may be out of date. If it isn't refreshing, you can manually inspect the bindings using bazel build //path/to:example_crate --config=crubit-genfiles. See Look at the generated bindings.
rust_library target {#rust_library}The first part of creating a library that can be used by Crubit is to write a rust_library target. For example:
cs/file:examples/rust/function/example.rs content:^[^/].*
In the BUILD file, in addition to defining the rust_library, you should also define the cpp_api_from_rust target (loaded via load("//bazel:cpp_api_from_rust.bzl", "cpp_api_from_rust")) to make it easier to use from C++:
cs/file:examples/rust/function/BUILD symbol:example_crate|example_crate_cc_api
Example: If your Rust library is named //path/to:example_crate, then the C++ header file is "path/to/example_crate.h", and the C++ namespace is example_crate by default.
C++ build rules do not have a rust_deps parameter, so to depend on the C++ bindings for a target, they must depend on the cpp_api_from_rust rule.
For example:
cs/file:examples/rust/function/BUILD symbol:main
cs/file:examples/rust/function/main.cc content:^[^/\n].*
NOTE: Other than for declaring the dependency, all other information about the generated bindings comes from the actual rust_library rule. For example, the #include for the above is #include "examples/rust/function/example_crate.h", not example_crate_cc_api.h.
The crate name might make a poor namespace. In addition, typically, multiple C++ headers and build targets share the same namespace. To customize the namespace name, use cc_bindings_from_rust_library_config:
cs/file:examples/rust/library_config/BUILD symbol:custom_namespace|example_crate
Now, instead of the crate name, the generated bindings will use the namespace name you provided:
cs/file:examples/rust/library_config/main.cc content:^[^/\n].*
The generated C++ API for a rust_library is a header named after the rust_library target, in the build output directory.
If Crubit can‘t generate bindings for a Rust item, the build doesn’t fail by default. Crubit leaves the item out of the generated header and writes a comment in its place explaining why:
// Error generating bindings for function `tuples_golden::param_option_in_tuple` // defined at // path/to/tuples.rs;l=370: // Error handling parameter #0 of type `(std::option::Option<i32>,)`: // crubit.rs/errors/bridge_compound_type: Tuples containing bridged type
The first error you see is usually in the C++ code that calls the missing item, which doesn‘t say why it’s missing. Search the generated header for the item's name to find the reason. The reason often names a type rather than the function itself; fix or wrap that type, and the function gets bindings too.
To make a missing item fail the build instead, annotate it with #[crubit_annotate::must_bind]. crubit_annotate is a procedural macro crate, so add //support:crubit_annotate to your rust_library's proc_macro_deps (see Attributes to fine-tune generated bindings).
Some features are either unsupported, or else only supported with experimental feature flags . In order to get bindings for a Rust interface, that interface must only use the subset of features currently supported.
For a particularly notable example, references are supported as function parameters and return types, but only in a subset of cases that we can prove does not add aliasing UB to C++ callers.
The way to work around this kind of problem, in all cases, is to wrap or hide the problematic interface behind an interface Crubit can handle:
HashMap<K, V> is not supported by Crubit, but pub struct MyStruct(HashMap<i32, i32>); is.