| # Rust bindings for C++ libraries |
| |
| When a C++ library enables Crubit, that library can be used directly from Rust. |
| 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/cpp/function/>. The other examples |
| in <https://github.com/google/crubit/tree/main/examples/cpp/> are also useful. If you |
| prefer just copy-pasting something, start there. |
| |
| ## How to use Crubit {#introduction} |
| |
| Crubit allows you to call some C++ interfaces from Rust. It supports |
| [functions](functions.md), [classes and structs](classes_and_structs.md), and |
| [enums](enums.md). Crubit does **not** support advanced features like templates |
| or virtual inheritance. |
| |
| The rest of this document goes over how to create a C++ library that can be |
| called from Rust, and how to actually call it from Rust. The quick summary is: |
| |
| 1. Define a `rust_api_from_cpp` target in the same `BUILD` file as your |
| `cc_library`. |
| |
| 2. Add the generated `.hint` target (e.g. `:<name_of_rust_target>.hint`) to the |
| `aspect_hints` of your `cc_library`. |
| |
| 3. Depend on the `rust_api_from_cpp` target in the `deps` of your Rust target. |
| |
| The bindings can be previewed using the following command: |
| |
| ```sh |
| $ bazel build --config=crubit-genfiles //path/to:target |
| ``` |
| |
| ### Write a `cc_library` target {#cc_library} |
| |
| The first part of creating a library that can be used by Crubit is to write a |
| `cc_library` target. For example: |
| |
| ``` |
| {{ #include ../../examples/cpp/function/example.h }} |
| ``` |
| <!-- --> |
| |
| |
| If you write a BUILD target as normal, it will not actually get Crubit bindings, |
| but we'll start from there: |
| |
| ``` |
| {{ #include ../../examples/cpp/function/BUILD }} |
| ``` |
| <!-- symbol:example_lib_broken --> |
| |
| |
| ### Enable Crubit on a target {#enable} |
| |
| To enable Crubit on a C++ target, you must define a `rust_api_from_cpp` target |
| associated with it, and link them using `aspect_hints`. |
| |
| Behind the scenes, Rust APIs are generated via Bazel aspects which run on the |
| `cc_library` target. When examining a `cc_library`, Rust API generation looks |
| for the `aspect_hints` so that it can find the corresponding `rust_api_from_cpp` |
| target. |
| |
| Define a `rust_api_from_cpp` target in the same `BUILD` file as your |
| `cc_library`, and add its `.hint` target to the `aspect_hints` of the |
| `cc_library`: |
| |
| ``` |
| {{ #include ../../examples/cpp/function/BUILD }} |
| ``` |
| <!-- symbol:\bexample_lib\b --> |
| |
| |
| ``` |
| {{ #include ../../examples/cpp/function/BUILD }} |
| ``` |
| <!-- symbol:\bexample_lib_rust\b --> |
| |
| |
| The `.hint` target is automatically created by the `rust_api_from_cpp` macro |
| (named `<name>.hint`) and is used to avoid circular dependencies between the C++ |
| library and the generated Rust API. |
| |
| Note that having Rust callers does constrain library evolution. Certain changes |
| cannot be made in C++ without breaking Rust callers, unless care is taken. |
| crubit.rs/cpp/cookbook#compatibility |
| |
| ### Look at the generated bindings {#examine} |
| |
| To examine the generated C++ bindings for the target, you can run the following |
| command: |
| |
| ```sh |
| $ bazel build --config=crubit-genfiles //examples/cpp/function:example_lib_broken |
| ``` |
| |
| This is the best way to preview the generated bindings for a given C++ target |
| right now. You might end up using this a lot, so keep it in your shell history. |
| |
| If you run the above command, you should see some output like the following: |
| |
| ``` |
| Aspect //rs_bindings_from_cc/bazel_support:rust_bindings_from_cc_aspect.bzl%rust_bindings_from_cc_aspect of //examples/cpp/function:example_lib up-to-date: |
| bazel-bin/examples/cpp/function/example_lib_rust_api_impl.cc |
| bazel-bin/examples/cpp/function/example_lib_rust_api.rs |
| bazel-bin/examples/cpp/function/example_lib_namespaces.json |
| ``` |
| |
| These files **are** the generated bindings which are used under the hood when |
| depending on a C++ target from Rust. They consist of: |
| |
| 1. The supporting C++ code to glue Rust and C++ together. (The `.cc` file.) |
| 2. The public Rust interface. (The `.rs` file.) |
| 3. Supporting internal implementation details. (The `.json` file.) |
| |
| You don't need to check them in, as they are regenerated automatically whenever |
| you build a Rust build target which depends on C++. |
| |
| The `.rs` file is the interesting one for end users. It should contain an |
| actually useful API for the target: |
| |
| ``` |
| {{ #include ../../examples/cpp/function/example_generated.rs }} |
| ``` |
| <!-- --> |
| |
| |
| |
| ### Use a C++ library from Rust {#use} |
| |
| To depend on a C++ library from Rust, add the corresponding `rust_api_from_cpp` |
| target to your Rust target's `deps`: |
| |
| ``` |
| {{ #include ../../examples/cpp/function/BUILD }} |
| ``` |
| <!-- symbol:main --> |
| |
| |
| At that point, the bindings are directly usable from Rust. The interface is |
| identical to the `.rs` file previewed earlier, but can be used directly: |
| |
| ``` |
| {{ #include ../../examples/cpp/function/main.rs }} |
| ``` |
| <!-- --> |
| |
| |
| ## Common Errors {#errors} |
| |
| See crubit.rs/errors |
| |
| ### Unsupported features |
| |
| Some features are either unsupported, or else only supported with experimental |
| feature flags |
| . In |
| order to get bindings for a C++ interface, that interface must only use the |
| subset of features currently supported. |
| |
| 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: |
| |
| * Hide unsupported types behind a wrapper. For example, a |
| `std::set<T>` is not supported, but a struct which wraps a `set::set<T>` is. |
| crubit.rs/errors/unsupported_type describes the process in more detail. |
| * Wrap unsupported functions, in general, behind wrappers. |
| * **Embed C++ directly in Rust**: Use [**`inline_cpp!`**](inline_cpp.md) to |
| allow embedding arbitrary C++ code within a Rust target using the |
| `rust_library_with_embedded_cpp` Bazel rule. |
| |
| [^aspects]: Crubit is an aspect: an automatically generated entity that exists |
| on every build target. It is disabled by default, so that Rust |
| callers don't accidentally impose on C++ libraries that weren't |
| expecting them. |
| |
| Aspects allow Crubit to fully understand the dependency graph: the |
| bindings for X are in the Crubit aspect of X. This allows Crubit to |
| generate bindings which themselves rely on bindings: if a function |
| in target `A` returns a struct from target `B`, we know that the |
| bindings for `A` will depend on the bindings for `B`. Because Crubit |
| is an aspect, it already knows the name of the bindings for `B`: |
| it's simply the Crubit aspect on `B`! |
| |
| Without aspects, or something like aspects, you would need to write |
| down, for every library, the location of its Rust bindings. There is |
| no need for that kind of boilerplate when aspects are involved, and |
| that is why most things shaped like Crubit use aspects. For example, |
| protocol buffers use aspects for their generated implementations in |
| multiple languages. (They *also* use named rules, but the rules |
| simply re-export the aspect, and the underlying aspect is what is |
| used within the rule for referring to transitive dependencies.) |
| Thanks to aspects, the `proto_library` doesn't need to re-specify |
| "ah, and the Go proto is named `'x'`". |
| |
| Be not afraid! Aspects are what make transitive dependencies work |
| seamlessly, without boilerplate. So when you see aspect this, or |
| aspect that, remember: this is a Good Thing. |