# prost: Protocol Buffers code generation for Rust, and where it stops

> prost turns proto2 and proto3 files into plain Rust structs with derive attributes, and deliberately omits runtime reflection. Here is what that buys you, what it costs, and how to get from a .proto file to a working build.

**tokio-rs/prost** — PROST! a Protocol Buffers implementation for the Rust Language

- Repository: https://github.com/tokio-rs/prost
- Stars: 4,775 · Forks: 643
- Language: Rust
- License: Apache-2.0
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/tokio-rs-prost

## The problem prost solves: proto files that produce Rust you would have written

Most Protocol Buffers toolchains for Rust generate code that looks machine-made. prost takes the opposite position. Its stated goal is to make the generated code as simple as possible, and the README lists the concrete consequences: it generates simple, idiomatic, readable Rust types by taking advantage of Rust derive attributes, retains comments from .proto files in the generated Rust code, and respects the Protobuf package specifier when organizing generated code into Rust modules. That last point matters more than it sounds. A package declaration of foo.bar becomes a foo::bar module, so the Rust module tree mirrors the proto namespace instead of collapsing everything into one flat file.

The second audience is people who already have Rust types. prost allows existing Rust types, not generated from a .proto, to be serialized and deserialized by adding attributes. That means you can put a Message derive on a struct you wrote by hand and speak the wire format without a code generation step at all. This is unusual among protobuf implementations and it is the feature that makes prost usable in codebases where the proto file is a contract with another service rather than the source of truth for your data model.

It is not a runtime library with a schema registry. prost is the generated types plus the encoding and decoding logic; prost-build is the piece that runs the compiler. The README is explicit that prost does not include support for runtime reflection or message descriptors. If your design depends on inspecting a message's schema at runtime, this is the wrong project and no amount of configuration changes that.

## How prost-build turns .proto files into modules

The mechanism is a build step, not a runtime one. prost-build invokes the protobuf compiler to produce a descriptor set, then generates Rust source from that descriptor. The generated code is ordinary .rs text that you include in your crate, so the wire format logic ships as compiled Rust with no schema file loaded at startup.

A few design decisions fall out of that pipeline. Serialization uses the bytes crate abstractions, Buf and BufMut, instead of std::io::Read and std::io::Write. That is a deliberate fit for async and buffer-oriented code: you encode into a buffer you already own rather than writing to a stream. It also means prost does not compose with APIs that expect std::io traits without an adapter.

Enum handling shows the same care about the wire format. Every .proto enumeration type converts to the Rust i32 type, and each enumeration type also gets a corresponding Rust enum. The generated enum type is not used directly as a field type. The README explains why: the Protobuf spec mandates that enumeration values are open, and decoding unrecognized enumeration values must be possible. So a field declared as PhoneType in proto becomes a pub r#type: i32 in Rust, with accessor methods that return the Rust enum. The getter methods return the enum's default value if the field holds an invalid i32. Unknown enum values are preserved during deserialization, which is what you want when an older client reads a message from a newer server.

Scalar mapping is a direct table: double to f64, float to f32, int32 to i32, uint32 to u32, sint32 to i32, fixed32 to u32, bool to bool, string to String, bytes to Vec<u8>. Field modifiers change the wrapper: in proto2, optional becomes Option<T> and required becomes T. In proto3, the default becomes T. None of this is configurable per field through a plugin option; it is the mapping prost commits to.

## Installing prost and compiling a first proto file

Add the runtime crate to your dependencies. The README gives the version line to use. prost-types is only necessary if you use Protobuf well-known types, so leave it out until you need Timestamp or Duration.

```toml
[dependencies]
prost = "0.14"
# Only necessary if using Protobuf well-known types:
prost-types = "0.14"
```

For the build side, the recommended way to add .proto compilation to a Cargo project is the prost-build library. Add it as a build dependency and call compile_protos from build.rs. Since prost-build v0.11, protoc is required to invoke that call unless skip_protoc is enabled. prost no longer provides a bundled protoc and does not attempt to compile one for you, so install the protobuf compiler separately and make sure it is on PATH. The README points at the protobuf project's own installation instructions for that step.

The first argument to compile_protos lists the proto files to compile and the second lists the include directories used to resolve imports. After a successful cargo build, the generated Rust source appears under the build output directory, and you pull it into your crate with an include! macro in a module. The README points to the snazzy repository for a simple start-to-finish example, which is the fastest way to see the whole wiring before you write your own build script.

For a sanity check on the generated shape, a message declared with a comment keeps that comment in the output. A proto message Foo with the comment "Sample message." becomes a struct carrying the same doc comment, deriving Clone, Debug, PartialEq, and Message. If your generated file does not have the comment, the build script is not reading the file you think it is.

## What prost will not do for you

The missing runtime reflection and message descriptors are the largest boundary. Anything that walks a schema at runtime, builds messages from a descriptor set, or exposes field names to a caller has to be built on top of something else, because prost's generated types carry no such metadata. The repository does contain a protobuf/ directory and a conformance/ crate in the workspace, but the README's own feature list is the authoritative statement about what the library exposes to users, and it says reflection is not included.

The protoc dependency is the second constraint, and it is a build-time one. With prost-build v0.11 and later, protoc is required to invoke compile_protos unless skip_protoc is enabled. If your build environment is hermetic, offline, or policy-restricted, you now own the job of getting a protobuf compiler into it. The skip_protoc option exists, but the README does not document what replaces protoc in that path, so treating it as a drop-in escape hatch would be a guess.

The third constraint is the minimum supported Rust version. prost keeps a rolling MSRV policy of at least 6 months: when the MSRV increases, the new Rust version must have been released at least six months earlier. The current MSRV is 1.85, and the workspace Cargo.toml sets rust-version = "1.85". The policy bounds how fast the floor moves, but it does move. A pinned old toolchain will eventually fail.

Finally, the maintenance signal in the README itself is a badge reading passively-maintained. The last push to the repository was on 2026-08-03. Read that badge as the maintainers describing their own level of engagement rather than a claim about the code's quality, and plan your dependency review accordingly.

## prost compared with the C++ protobuf toolchain and tonic

The most direct alternative for Rust users is the C++ protobuf implementation plus its Rust bindings, which take a runtime-centric approach: the schema is loaded and interpreted, reflection and descriptors are available, and generated accessors go through a message object rather than public struct fields. The practical difference is where the schema lives. With prost, the schema is consumed at build time and disappears from the binary as data; with a reflection-based implementation, descriptor information is available to the running program. If you need to inspect or construct messages dynamically, prost's model is the wrong one, and that is a design choice rather than a gap to be patched.

Within the Rust ecosystem, prost is the code generation layer and tonic is the gRPC layer built on top of it. Choosing tonic is not an alternative to prost so much as a commitment to prost plus HTTP/2 and service definitions. If you only need to encode and decode messages, prost alone is the smaller dependency.

The bytes-based serialization also separates prost from implementations that build on std::io. If your code is already written around Buf and BufMut, prost fits without an adapter. If it is written around Read and Write, you are the one writing the glue. Neither choice is universally better; they simply assume different surrounding code.

## Licence, releases and the cost of upgrading

prost is licensed Apache-2.0, and the workspace Cargo.toml declares license = "Apache-2.0" for the workspace package. That is a permissive licence with an explicit patent grant, and it is compatible with the usual expectations for a Rust library dependency. This is a description of what the repository states, not legal advice; if your organisation has a licence review process, run it.

Upgrade cost is dominated by two things. The first is the MSRV floor, currently 1.85. Raising your toolchain is a prerequisite for taking a new prost release, and the six-month policy means the floor will not jump arbitrarily, but it will not stay put either. The second is generated code churn. Because prost generates plain Rust source into your build directory, a version bump can change the shape of the generated types, and any code you wrote against those types is affected. The workspace ships a CHANGELOG.md and a prepare-release.sh and publish-release.sh pair, and the release history is regular: v0.14.2 on 2025-12-01, v0.14.3 on 2026-01-10, and v0.14.4 on 2026-06-07. Read the changelog for the versions you cross rather than only the latest one.

The conformance crate in the workspace suggests the project tests against the protobuf conformance suite, and the fuzz/ directory and FUZZING.md indicate a fuzzing setup. Those are reasons to trust the wire format implementation, but they are not a promise about API stability between minor versions.

## Conclusion

Adopt prost if you want generated Rust structs that look hand-written and you can accept a build-time protoc dependency plus no runtime reflection or message descriptors. Do not adopt it if your architecture needs dynamic message handling, descriptor-driven tooling, or a bundled compiler in the build. Before committing, verify two things in your own tree: that protoc is present on every machine and CI runner that calls compile_protos, and that your .proto files avoid the constructs the generated code cannot express cleanly, particularly required fields and extensions. Check the CHANGELOG.md of the workspace at the version you pin, since the workspace version is 0.14.4 and the MSRV of 1.85 is a hard floor for your toolchain.

## FAQ

### What is the difference between prost and the C++ Protocol Buffers implementation for Rust?

prost generates idiomatic Rust types using derive attributes and, per its README, does not include support for runtime reflection or message descriptors. A reflection-based implementation keeps descriptor information available at runtime, which prost deliberately gives up in exchange for simpler generated code.

### Does prost require protoc to be installed?

Yes, for the build path. The README states that with prost-build v0.11, protoc will be required to invoke compile_protos unless skip_protoc is enabled, and that prost will no longer provide a bundled protoc or attempt to compile one for users.

### What is the minimum supported Rust version for prost?

The current MSRV is 1.85, and the workspace Cargo.toml sets rust-version = "1.85". The README describes a rolling MSRV policy of at least 6 months, where a new Rust version must have been released at least six months before it is adopted.

### Can prost serialize Rust types that were not generated from a .proto file?

Yes. The README lists this as a distinguishing feature: existing Rust types can be serialized and deserialized by adding attributes, without going through code generation.

## Sources

- [Issues](https://github.com/tokio-rs/prost/issues)
- [License: Apache-2.0](https://github.com/tokio-rs/prost/blob/master/LICENSE)
- [README](https://github.com/tokio-rs/prost/blob/master/README.md)
- [Releases](https://github.com/tokio-rs/prost/releases)
- [tokio-rs/prost on GitHub](https://github.com/tokio-rs/prost)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/tokio-rs-prost
