blob: e68f0d521e0e699395439eacd81e5a789d7a4343 [file] [view]
<!-- <internal link> -->
# C++/Rust Protobuf interop
WARNING: This page documents functionality that is currently internal to the
Google monorepo.
Protocol buffers are Google's language-neutral, platform-neutral, extensible
mechanism for serializing structured data. Once you define how you want your
data to be structured once, you can generate source code in a variety of
languages to manipulate and serialize/deserialize your structured data. Protobuf
messages are among the most common types at Google, appearing in vast majority
of APIs.
The usual way to passing data from one language to another using Protobufs is to
serialize a message in one language, and deserialize it in another. This
serialization/deserialization has costs which makes this approach unsuitable for
hot code paths.
To avoid those costs, we've intentionally designed C++ and Rust Protobuf message
types to have identical layouts. We avoid the need for
serialization/deserialization and instead we directly use the same message
object from both languages. Crubit automatically generates the zero-cost glue
code for us. For example, take this piece of a C++ header:
```c++
MyProto Foo();
```
This becomes available to Rust as:
```rust
pub fn Foo() -> MyProto {...}
```
(Specifically, Crubit will detect that this is a Protobuf message, and it will
convert from the C++ message type to the Rust message type.)
## Calling Rust APIs using Protobuf message types {#rust}
| Rust | C++ |
| :------------ | :--------------- |
| `Message` | `Message` |
| `MessageView` | `const Message*` |
| `MessageMut` | `Message*` |
Protocol buffers are supported by value, and using the `View` and `Mut` view
types, where they are mapped to C++ pointers.
See
cc_bindings_from_rs/test/bridging/protobuf/rust_lib.rs
for an example definition, and
cc_bindings_from_rs/test/bridging/protobuf/user_of_rust_lib.cc
for how to call it from Rust.
## Calling C++ APIs using Protobuf message types {#cpp}
Calling C++ APIs which use protobuf is slightly more difficult.
First of all, add your `proto_library` target to the
[allowlist](http://<internal link>). See b/414381884 for more context
& information on when this allowlist will be removed.
### Passing by value
| C++ | Rust |
| :-------- | :-------- |
| `Message` | `Message` |
When a C++ proto message is passed or returned by value, it is mapped directly
to the Rust message type, as you would expect.
C++:
```live-snippet
cs/file:google_internal/protobuf/by_value.h content:foo::Message
```
Rust:
```live-snippet
cs/file:google_internal/protobuf/by_value_test.rs content:by_value\:\:|\bmsg\b
```
### Passing by reference
| C++ | Rust |
| :--------------------------------- | :------------------------------------------- |
| `const Message*`, `const Message&` | `*const Incomplete<symbol!("Message"), ...>` |
| `Message*`, `Message&` | `*mut Incomplete<symbol!("Message"), ...>` |
When a C++ proto is passed by pointer or by reference, the Rust type is a
pointer to a forward declaration of the C++ protocol buffer type.
In particular, C++ APIs are **not** exposed using the `View` or `Mut` types.
These are pointers because C++ APIs do not annotate ownership, lifetime, or
aliasing properties, and so these cannot be mapped to the distinct owned,
`View`, or `Mut` types of the Rust protobuf API. And these are forward-declared
because the C++ types do not have direct Rust bindings: the generated `.proto.h`
file does not get piped through Crubit.
* **To convert a Rust `Proto` to a C++ `const Proto*`**: use
`my_proto.as_view().cpp_cast()`
* **To convert a Rust `Proto` to a C++ `Proto*`**: use
`my_proto.as_mut().cpp_cast()`
* **To convert a C++ `(const) Proto*` to a Rust `View`/`Mut`**: use `unsafe
{my_ptr.unsafe_cpp_cast()}`.
See support/forward_declare.rs for the definition of
`Incomplete`, `CppCast`, and `UnsafeCppCast`.
For copy-pastable example code, see the examples in
google_internal/protobuf/