blob: 6c822d1ae8ab274d0711bb98bf8c82b8ec90515c [file] [view] [edit]
# C++ bindings for Rust libraries
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.
## How to use Crubit {#introduction}
Crubit allows you to call some Rust interfaces from C++. It supports
[functions](functions.md) (including methods), [structs](structs.md),
[traits](traits.md), and even [enums](enums.md) 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
`cc_bindings_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:my_cc_bindings_from_rust_target --config=crubit-genfiles`. This
will generate bindings and print the paths of generated files to stdout.
### Write a `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:
```
{{ #include ../../examples/rust/function/example.rs }}
```
<!-- content:^[^/].* -->
In the BUILD file, in addition to defining the `rust_library`, you should also
define the `cc_bindings_from_rust` target to make it easier to use from C++:
```
{{ #include ../../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.
### Use a Rust library from C++ {#use}
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 `cc_bindings_from_rust` rule.
For example:
```
{{ #include ../../examples/rust/function/BUILD }}
```
<!-- symbol:main -->
```
{{ #include ../../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`.
### (Optional) Customize the generated C++ API {#cc_bindings_from_rust_library_config}
#### Give it a better namespace {#namespace}
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`:
```
{{ #include ../../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:
```
{{ #include ../../examples/rust/library_config/main.cc }}
```
<!-- content:^[^/\n].* -->
### Look at the generated bindings {#examine}
There are two ways to look at the generated header file:
* Click through the `#include` in Cider. Given the following C++ code:
```c++
#include "path/to/example_crate.h"
```
If you right click the file path, and select "Go to Definition", you will be
taken to a file starting with `// Automatically @generated C++ bindings`.
* Run `bazel build //path/to:example_crate --config=crubit-genfiles`, and open
`bazel-bin/path/to/example_crate.h` in your text editor of choice.
## Common Errors {#errors}
### Unsupported features
Some features are either unsupported, or else only supported with experimental
feature flags (crubit.rs-features). 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 only supported as function
parameters, and 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:
* Use raw pointers instead of references, if this use of references falls into
a case Crubit does not support.
* Hide unsupported types behind a wrapper type. For example, a `Vec<T>` is not
supported by Crubit, but `pub struct MyStruct(Vec<i32>);` is.