Live data from Hacker News

Zero-copy protobuf and ConnectRPC for Rust

medium.com

21–30 of 47 posts

Re: Zero-copy protobuf and ConnectRPC for Rust

#21
I previously worked at Bytedance and we've maintained a Rust zero-copy gRPC/Thrift implementation for 4 years: https://github.com/cloudwego/volo, it is based on Bytes crate (reference counting bytes, for folks don't familiar with Rust ecosystem). A fun fact: when we measuring on our product environment, zero-copy isn't means higher performance in lots of scenarios, there are some trade-offs:

1. zero-copy means bytes are always inlined in the raw message buffer, which means the app should always access bytes by a reference/pointer

2. You cannot compress the RPC message, if you want to fully leverage the advantages from zero serdes/copy

3. RC itself

Re: Zero-copy protobuf and ConnectRPC for Rust

#22

True zero-copy is not achievable with Protobuf, you need something like FlatBuffers for that. What is presented here is more like a zero-allocations.

Is this still true? New versions of protobuf allow codegen of `std::string_view` rather than `const std::string&` (which forces a copy) of `string` and `repeated byte` fields. https://protobuf.dev/reference/cpp/string-view/

It allows avoiding allocations, but it doesn't allow using serialised data as a backing memory for an in-language type. Protobuf varints have to be decoded and written out somewhere. They cannot be lazily decoded efficiently either: order of fields in the serialised message is unspecified, hence it either need to iterate message over and over finding one on demand or build a map of offsets, which negates any wins zero-copy strives to achieve.

Re: Zero-copy protobuf and ConnectRPC for Rust

#23

True zero-copy is not achievable with Protobuf, you need something like FlatBuffers for that. What is presented here is more like a zero-allocations.

Is this still true? New versions of protobuf allow codegen of `std::string_view` rather than `const std::string&` (which forces a copy) of `string` and `repeated byte` fields. https://protobuf.dev/reference/cpp/string-view/

Those field accessors take and return string_view but they still copy. The official C++ library always owns the data internally and never aliases except in one niche use case: the field type is Cord, the input is large and meets some other criteria, and the caller had used kParseWithAliasing, which is undocumented.

To a very close approximation you can say that the official protobuf C++ library always copies and owns strings.

Re: Zero-copy protobuf and ConnectRPC for Rust

#24
post #10
post #5

Earlier quoted context omitted.

No HTTP, Proto, or gRPC crate should ever find itself in the stdlib. Didn't we learn this with python? How many python http client libraries are in the dumping ground that is the python "batteries included" standard library? And yet people always reach for the one that is outside stdlib.

On the other hands, having half the packages depend on packages such as serde, syn, procmacro2 might not be such a good idea. First of all it is annoying when creating new projects to have to move over table stakes. Second, it is a security nightmare. most of rust could be vulnerable if dtolnay decided to go rogue. It is not that everything should go into the stdlib, but having syn, procmacro and serde would be a goo…

I agree with this. Rust has a node-style dependency problem; any non-trivial rust project ends up with dozens of dependencies in my experience. I would add tokio to the list of dependencies-so-common-they-should-be-moved-to-stdin.

A second tier stdlib would turn out like the Boost c++ libraries -- an 800 lb gorilla of a common dependency that gets called in just to do something very simple; although to be fair most of the Boost functionality already is in rust's stdlib.

Re: Zero-copy protobuf and ConnectRPC for Rust

#25
post #8
post #5

Earlier quoted context omitted.

No HTTP, Proto, or gRPC crate should ever find itself in the stdlib. Didn't we learn this with python? How many python http client libraries are in the dumping ground that is the python "batteries included" standard library? And yet people always reach for the one that is outside stdlib.

You don't think golang's http library is a good idea? I would have thought everyone is happy we have it

Would it be still a good idea if instead of being created / owned by google as an organization it was originally made by someone that didn't make billions by handling trillions of http requests over decades and you had to keep all of the bad initial api design choices going forward?

Re: Zero-copy protobuf and ConnectRPC for Rust

#26
post #23

Earlier quoted context omitted.

Is this still true? New versions of protobuf allow codegen of `std::string_view` rather than `const std::string&` (which forces a copy) of `string` and `repeated byte` fields. https://protobuf.dev/reference/cpp/string-view/

Those field accessors take and return string_view but they still copy. The official C++ library always owns the data internally and never aliases except in one niche use case: the field type is Cord, the input is large and meets some other criteria, and the caller had used kParseWithAliasing, which is undocumented. To a very close approximation you can say that the official protobuf C++ library always copies and owns…

Well that is very disappointing news.

Even the decoder makes a copy even though it's returning a string_view? What's the point then.

I can understand encoders having to make copies, but not in a decoder.

Re: Zero-copy protobuf and ConnectRPC for Rust

#27
post #21

I previously worked at Bytedance and we've maintained a Rust zero-copy gRPC/Thrift implementation for 4 years: https://github.com/cloudwego/volo , it is based on Bytes crate (reference counting bytes, for folks don't familiar with Rust ecosystem). A fun fact: when we measuring on our product environment, zero-copy isn't means higher performance in lots of scenarios, there are some trade-offs: 1. zero-copy means bytes…

Speaking of volo I'm trying to implement a etcd shim with SurrealKV. Haven't been able to get the OG etcd E2E conformance test 100% passed yet so I'm not releasing it just now

Re: Zero-copy protobuf and ConnectRPC for Rust

#28

It's 2026 and I'm still defining my own messaging and wire protocols. Plain C structs that fit in a UDP datagram that you can reinterpret_cast from is still best. You can still provide schemas and UUIDs for that, and dynamically transcode to JSON or whatever.

Provided that:

    - you agree never to care about endianness (can probably get away with this now)

    - you don't want to represent anything complicated or variable length, including strings

Re: Zero-copy protobuf and ConnectRPC for Rust

#29
post #10

Earlier quoted context omitted.

On the other hands, having half the packages depend on packages such as serde, syn, procmacro2 might not be such a good idea. First of all it is annoying when creating new projects to have to move over table stakes. Second, it is a security nightmare. most of rust could be vulnerable if dtolnay decided to go rogue. It is not that everything should go into the stdlib, but having syn, procmacro and serde would be a goo…

I agree with this. Rust has a node-style dependency problem; any non-trivial rust project ends up with dozens of dependencies in my experience. I would add tokio to the list of dependencies-so-common-they-should-be-moved-to-stdin. A second tier stdlib would turn out like the Boost c++ libraries -- an 800 lb gorilla of a common dependency that gets called in just to do something very simple; although to be fair most o…

As long as the "2nd-tier" stdlib was versioned & tied in with the edition system, it could work. The problem with most stdlibs (including Rust's) is that there's no way to remove anything & replace it with a better design. So the lib only ever grows, slowly adding complexity.

Re: Zero-copy protobuf and ConnectRPC for Rust

#30

It's 2026 and I'm still defining my own messaging and wire protocols. Plain C structs that fit in a UDP datagram that you can reinterpret_cast from is still best. You can still provide schemas and UUIDs for that, and dynamically transcode to JSON or whatever.

Until you have to work with big and little endian systems. There are other weirdness about how different computers represent things as well. utf-8 / ucs-16 strings (or other code pages). Not all floats are ieee-754. Still when you can ignore all those issues what you did is really easy and often works.
Post reply on HN