The Rust implementation of Protocol Buffers provides idiomatic Rust bindings to Protocol Buffers, enabling developers to use Protocol Buffers in Rust projects. This implementation is designed with a dual-kernel architecture, allowing it to leverage either the high-performance C++ runtime or the lightweight upb (micro-protobuf) C runtime while exposing a unified Rust API.
The Protocol Buffers Rust implementation consists of two main parts:
protoc).The Rust implementation supports two different kernels (underlying implementations):
Sources: rust/BUILD19-45 rust/shared.rs1-113 src/google/protobuf/compiler/rust/generator.cc115-150
The Rust implementation uses a "dual kernel" architecture where the public API is identical regardless of the underlying backend. This is achieved through conditional compilation and a common set of traits.
| Kernel | Rust Cfg | Backend Description | Rust Library Target |
|---|---|---|---|
| C++ | cpp_kernel | Uses C++ Message and RepeatedField via FFI "thunks". | :protobuf_cpp |
| upb | upb_kernel | Uses upb_Message and upb_Array with arena allocation. | :protobuf_upb |
The rust_library target protobuf acts as a facade, selecting the kernel based on the Bazel build setting :use_upb_kernel.
Sources: rust/BUILD32-45 rust/BUILD103-131 rust/BUILD163-189
The core of the Rust API is the Proxied trait system. Because Protocol Buffers data is often owned by a C++ or upb heap, Rust cannot always use standard references. Instead, it uses "Proxy" types:
View<'msg, T>: A read-only view of a field or message, conceptually similar to &T.Mut<'msg, T>: A mutable view of a field or message, conceptually similar to &mut T.Proxied: The base trait implemented by all types that can be stored in a proto message.Sources: rust/proxied.rs25-33 rust/proxied.rs128-150 rust/shared.rs31-33
The Rust code generator (src/google/protobuf/compiler/rust/) produces Rust source files (.pb.rs) and, for the C++ kernel, C++ "thunk" files (.pb.thunks.cc).
For every message in a .proto file, the generator creates three primary structs:
MyMessage: The owned message (implements SettableValue, Clear, etc.).MyMessageView: The read-only proxy.MyMessageMut: The mutable proxy.The implementation of these structs varies by kernel. For example, MyMessage::new() for C++ calls an external thunk, while for upb it initializes a new OwnedMessageInner which manages a upb arena.
Sources: src/google/protobuf/compiler/rust/message.cc44-62 src/google/protobuf/compiler/rust/message.cc108-146
Accessors are generated based on the field type (singular, repeated, map) and presence rules.
get_message_at_index for upb or a get thunk for C++.View (containing data) and one for the Case (just the tag).Sources: src/google/protobuf/compiler/rust/accessors/singular_message.cc26-69 src/google/protobuf/compiler/rust/oneof.cc124-159 src/google/protobuf/compiler/rust/naming.cc94-116
Repeated fields are implemented via the Repeated<T> struct. It stores an InnerRepeated, which is a pointer to a RepeatedField<T> in C++ or a upb_Array in upb.
RepeatedView<'msg, T>: Provides len(), is_empty(), and get(index).RepeatedMut<'msg, T>: Adds push(val), set(index, val), and clear().Sources: rust/repeated.rs28-31 rust/repeated.rs168-225 rust/repeated.rs358-400
Maps use the Map<K, V> struct. Like repeated fields, they wrap a RawMap.
MapValue trait defines how to interact with map entries (insert, get, remove, iter) across kernels.MapKey trait restricts which types can be used as keys (e.g., no floating point or messages).Sources: rust/map.rs23-73 rust/map.rs75-78 rust/map.rs212-241
The implementation provides Bazel rules to automate code generation and linking:
rust_upb_proto_library: Generates code using the upb kernel.rust_cc_proto_library: Generates code using the C++ kernel.rust_proto_library: A macro that wraps the specific kernel rules.Sources: rust/defs.bzl12-15 rust/test/BUILD51-73
For non-Bazel environments, the project includes a structure for releasing crates.
protobuf crate: The main runtime.protobuf_codegen crate: The code generation logic for use in build.rs.Sources: rust/release_crates/protobuf/Cargo-template.toml1-10 rust/release_crates/protobuf_codegen/src/lib.rs1-15
When using the C++ kernel, the Rust code cannot call C++ member functions directly. The generator emits a C++ file containing extern "C" functions (thunks) that wrap the C++ API.
Sources: src/google/protobuf/compiler/rust/naming.cc94-100 src/google/protobuf/compiler/rust/accessors/singular_message.cc162-188