Try out gin-tonic - prost alternative
⚓ Rust 📅 2026-05-17 👤 surdeus 👁️ 2gin-tonic
gin-tonic is a Rust protobuf library that lets you use your own types directly on the wire — no manual conversion boilerplate.
It provides:
- Protobuf serialization and deserialization (like
prost) - A code generator replacing
prost-build - A
toniccodec implementation - A wrapper for
tonic-buildwith extra features - A
Scalartrait to map any Rust type directly to a protobuf wire type
The problem with other libraries
When you use a UUID in a protobuf message with prost, you write:
message Foo {
string my_uuid = 1;
}
This generates:
struct Foo {
my_uuid: String,
}
Your code wants uuid::Uuid, so you end up writing conversions everywhere — and handling parse errors at every call site.
The gin-tonic approach
Annotate your .proto file with the Rust type you want:
import "gin/proto/gin.proto";
message Foo {
string my_uuid = 1 [(gin_tonic.v1.rust_type) = "uuid::Uuid"];
}
The gin-tonic code generator produces:
struct Foo {
my_uuid: uuid::Uuid,
}
The conversion is handled once, inside the Scalar trait implementation — not scattered across your codebase.
Built-in UUID support
Two feature flags cover the UUID case out of the box:
| Feature | Wire type | Notes |
|---|---|---|
uuid_string |
string |
Parse errors handled in the wire type conversion |
uuid_bytes |
bytes |
No parse errors; 16-byte fixed representation |
Custom types
Implement Scalar for any type to use it as a protobuf field:
impl gin_tonic_core::Scalar<gin_tonic_core::scalars::ProtoString> for MyType {
const WIRE_TYPE: u8 = gin_tonic_core::WIRE_TYPE_LENGTH_ENCODED;
fn encode(&self, encoder: &mut impl gin_tonic_core::Encode) {
encoder.encode_str(&self.to_string());
}
fn decode(decoder: &mut impl gin_tonic_core::Decode) -> Result<Self, gin_tonic_core::ProtoError> {
decoder.decode_string()?.parse().map_err(Into::into)
}
}
Benchmarks
Measured against prost 0.14.3 on an equivalent message with a UUID, 10 IP addresses, a string, and a nested map with 5 entries on AMD Ryzen AI 7 350.
gin-tonic:
gin_encode time: [1.0944 µs 1.0957 µs 1.0974 µs]
gin_decode time: [2.4878 µs 2.4886 µs 2.4893 µs]
prost (including From conversions to idiomatic Rust types):
prost_encode time: [2.3426 µs 2.3477 µs 2.3568 µs]
prost_decode time: [2.7674 µs 2.7731 µs 2.7772 µs]
Decode performance is slightly better while encoding is about twice as fast.
Crates
| Crate | Description |
|---|---|
gin-tonic |
Main crate — re-exports everything, includes code generator and tonic codec |
gin-tonic-core |
Runtime traits and serialization primitives |
gin-tonic-derive |
Derive macros (Message, Enumeration, OneOf) |
1 post - 1 participant
🏷️ Rust_feed