NOW LET US – AI RAG SaaS Studio TP.HCM
NOW LET US
Digital Product Studio
Back to news
DEV-TOOLS...4 min read

Zero-copy protobuf and ConnectRPC for Rust

Share
NOW LET US Article – Zero-copy protobuf and ConnectRPC for Rust

Anthropic has open-sourced buffa and connectrpc, two Rust crates that introduce first-class Protobuf editions support and zero-copy message views to the RPC ecosystem.

As part of my work at Anthropic, I open sourced two Rust crates that fill a gap in the RPC ecosystem: buffa, a pure-Rust Protocol Buffers implementation with first-class editions support and zero-copy message views, and connectrpc, a Tower-based ConnectRPC implementation that speaks Connect, gRPC, and gRPC-Web on the same handlers. We’re nominating connect-rust as the canonical Rust implementation of ConnectRPC — if you’re using Connect from Go, TypeScript, or Kotlin, this is intended to be the peer implementation for Rust. This code is already in production at Anthropic. For those who want to jump straight to the code, see buffa and connect-rust on GitHub.

Both crates pass their full upstream conformance suites — Google’s protobuf binary and JSON conformance for buffa, and all ~12,800 ConnectRPC server, client, and TLS tests for connect-rust — though as I’ll cover later, a green conformance run turned out to be necessary but far from sufficient for production. They were built in six weeks with Claude Opus 4.6 doing most of the work under my direction — an experiment in specification-driven development for performance- and correctness-sensitive library code.

This post covers the Rust-specific design decisions: how protobuf editions map to codegen, why zero-copy views need an OwnedView escape hatch, the type-level choices for mapping protobuf’s semantics onto Rust, and what the conformance suites didn’t catch. A separate post on the AI-assisted development process will follow.

Why another protobuf crate?

The short answer: editions, and leaning into the specific capabilities of Rust.

The schism caused by proto2 / proto3 semantic divergence is being healed by switching to a feature-flag-driven approach to the wire format, defined by editions. Each edition specifies a default feature set. Messages defined in files from older editions (e.g. proto2) can be used from newer editions. If you are defining new message types, these details are mostly irrelevant, but if you are porting legacy systems from the proto2 era, this is likely to make your migration significantly easier.

The Rust ecosystem hasn’t caught up. Prost is the de facto standard, and it’s excellent at what it does — but it targets binary proto3, with JSON bolted on via pbjson, and the library is now only passively maintained. Google’s official Rust implementation (protobuf v4) supports editions but is built around upb, so it needs a C compiler and there is not yet an RPC layer implementation above it.

Buffa treats editions as the core abstraction, and is also designed to work well with the current best available tooling: buf CLI for language-agnostic code generation (though protoc is of course also supported), a buffa-build crate for build.rs integration for those who prefer cargo-oriented build pipelines, and careful definition of crate features and generated code that allow the library to be used in no_std, or to select the features that matter to your use case (e.g. excluding JSON support).

Zero-copy message views

Rust provides an interesting opportunity that does not exist for implementations in other languages: we can support message “views” where data does not need to be copied from an input buffer to be used, reducing allocation cost.

The need for this wasn’t purely speculative. In an early prototype of connect-rust that used prost, profiling showed that per-field String allocation and HashMap construction for map fields significantly contributed to allocator pressure. For string and bytes fields, copying data is avoidable and safe with Rust’s borrow checker, referencing the content directly in the input buffer.

Buffa generates two types per message: MyMessage (owned, heap-allocated, similar to what you’d expect in most implementations) and MyMessageView<'a> (borrows directly from the wire buffer). The view type’s string fields are &'a str, its bytes fields are &'a [u8], and its map fields are a flat Vec<(K, V)> scan — no hashing on the decode path.

Configurable safety controls

There are some aspects of protobuf that can be unsafe or enable attacks when used in an RPC framework that deserve special consideration. Depending on your use case and environment, it is useful to be able to tune the safety controls around these issues.

Buffa provides a DecodeOptions type to control both recursion limits and message size. Prost enforces a fixed recursion limit of 100 nested messages; buffa uses the same default, but allows for overriding this via with_recursion_limit(n) to a smaller or larger value as needed. For message length, Prost does not apply a limit, while buffa provides control at the protobuf level, with a default that matches the protobuf spec (2 GiB). The connect-rust library applies a 4 MiB default limit for messages and HTTP bodies that is more typical for HTTP servers.

Rust String / &str values must be valid UTF-8, whereas proto2 strings do not have this restriction and later editions provide an opt-out for UTF-8 verification. Regardless, the natural user expectation is that a string field should be a String in the Rust struct, so buffa chooses to perform UTF-8 validation for all strings by default. The library also provides an opt-out that changes string fields with utf8_validation = NONE to Vec<u8> / &[u8] instead, allowing validation during decode to be bypassed without misleading the user as to the safety of the content.

Ergonomics

Protobuf makes some very opinionated choices around message semantics, which can be quite different from the typical behavior of primitive data types in most languages. Two examples of this semantic mismatch that require careful resolution in Rust are optional message fields and enums.

Message fields have default value semantics, that combined with recursive message types, can be difficult to represent cleanly. Prost uses Option<M> or Option<Box<M>> for optional message fields, depending on whether the message type is recursive. Buffa defines a MessageField<T> that handles all message fields, and this provides Deref and From trait implementations. This produces more natural field interaction.

© 2026 Now Let Us. All rights reserved.

Source: Hacker News

Advertisement
Ad slot ready: 5887729102

More in this category

EXPLORE TOPICS

Discover All Categories

Deep dive into the specific technology sectors that matter most to you.