| # Rust bindings for C++ libraries | |
| [TOC] | |
| 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 | |
| examples/cpp/function/. The other examples in | |
| examples/cpp/ are also useful. If you prefer just | |
| copy-pasting something, start there. | |
| ## How to use Crubit {#introduction} | |
| Crubit allows you to call a C-like interface from Rust. That is, an interface | |
| where all [functions are `extern "C"`](functions), | |
| [classes and structs are rust-movable](classes_and_structs), and there are no | |
| 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. A `cc_library` gets (nonempty) Rust bindings if it specifies `aspect_hints = | |
| ["//features:supported"]`. | |
| 2. Any Rust build target can depend on the bindings for a `cc_library`, by | |
| specifying `cc_deps=["//path/to:target"]`. | |
| 3. 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: | |
| ```live-snippet | |
| cs/file: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: | |
| ```live-snippet | |
| cs/file:examples/cpp/function/BUILD symbol:example_lib_broken | |
| ``` | |
| ### Look at the generated bindings {#examine} | |
| Bindings can be generated for any C++ target, anywhere in the build graph. | |
| (Crubit is an **aspect**[^aspects] on all C++ targets.) However, that is not to | |
| say that the generated bindings will be useful: by default, Crubit doesn't | |
| generate any bindings. Try it! | |
| 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_broken up-to-date: | |
| bazel-bin/examples/cpp/function/example_lib_broken_rust_api_impl.cc | |
| bazel-bin/examples/cpp/function/example_lib_broken_rust_api.rs | |
| bazel-bin/examples/cpp/function/example_lib_broken_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 information that is used by bindings that *depend* on these | |
| bindings. (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. For a library like `:example_lib_broken`, which does not enable Crubit, the | |
| `.rs` file will be essentially empty, only consisting of comments describing the | |
| bindings it did not generate: | |
| ```rust | |
| // Generated from: examples/cpp/function/example.h;l=11 | |
| // Error while generating bindings for item 'crubit_add_two_integers': | |
| // Can't generate bindings for crubit_add_two_integers, because of missing required features (<internal link>): | |
| // //examples/cpp/function:example_lib_broken needs [//features:supported] for crubit_add_two_integers (return type) | |
| // //examples/cpp/function:example_lib_broken needs [//features:supported] for crubit_add_two_integers (the type of x (parameter #0)) | |
| // //examples/cpp/function:example_lib_broken needs [//features:supported] for crubit_add_two_integers (the type of y (parameter #1)) | |
| // //examples/cpp/function:example_lib_broken needs [//features:supported] for crubit_add_two_integers (extern \"C\" function) | |
| ``` | |
| This error is saying something important. It was trying to generate bindings for | |
| the function `crubit_add_two_integers`, but it couldn't, because four different | |
| things about the function require the `supported` feature to be enabled on the | |
| target. The parameter and return types require `supported`, as does the function | |
| itself in the abstract (as it is an `extern "C"` function). | |
| `supported` indicates that a library target supports Rust callers via Crubit, | |
| using the stable features. (Temporarily, you may see references to `extern_c` -- | |
| these are part of `supported`). Other functions and classes might | |
| require `experimental`, for experimental features of Crubit. For example, if we | |
| had defined an`operator+`, or if the function were not`extern "C"`. For more on | |
| this, see <internal link>. | |
| ### Enable Crubit on a target {#enable} | |
| To enable Crubit on a C++ target, one must pass an argument, via `aspect_hints`. | |
| Specifically, as mentioned in the comments, the target must enable the | |
| `supported` feature: | |
| ```live-snippet | |
| cs/file:examples/cpp/function/BUILD symbol:\bexample_lib\b | |
| ``` | |
| This tells Crubit that it can generate bindings for this target, for any part of | |
| the library that uses features from `supported`. Now, if we look at a preview of | |
| the automatically generated bindings: | |
| ```sh | |
| $ bazel build --config=crubit-genfiles //examples/cpp/function:example_lib | |
| ``` | |
| We can see the fully-fledged bindings for the library: | |
| ```live-snippet | |
| cs/file:examples/cpp/function/example_generated.rs | |
| ``` | |
| ### Use a C++ library from Rust {#use} | |
| To depend on a C++ library from Rust, add it to `cc_deps`: | |
| ```live-snippet | |
| cs/file: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: | |
| ```live-snippet | |
| cs/file:examples/cpp/function/main.rs | |
| ``` | |
| ## Common Errors {#errors} | |
| ### Unsupported features | |
| Some features are either unsupported, or else only supported with experimental | |
| feature flags (<internal link>). In order to get bindings for a C++ | |
| interface, that interface must only use the subset of features currently | |
| supported. | |
| For a particularly notable example, a class cannot have a `std::string` field, | |
| because `std::string` has properties around move semantics that Crubit does not | |
| yet support. In turn, this means the class *containing* the `std::string` has | |
| semantics that Crubit doesn't yet support. | |
| 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: | |
| * Move nontrivial types behind a `unique_ptr<T>`. A `std::string` field is not | |
| rust-movable, but a `unique_ptr<std::string>` field is. | |
| * Hide unsupported types, in general, behind a wrapper. For example, a | |
| `std::vector<T>` is not supported, but a struct which wraps a | |
| `unique_ptr<std::vector<T>>` is. | |
| * Wrap unsupported functions behind `extern "C"` wrappers. For example, | |
| methods are not yet supported, but top-level functions are, and can invoke | |
| methods. | |
| [^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. |