← Week 1: Protocol Buffers & gRPC

Day 2: Protobuf in Rust with prost

Phase 3 · Jul 2, 2026

← Week 1: Protocol Buffers & gRPC

Agenda (2–3 hours)

  • Read (45 min): prost crate documentation; prost-build README; tonic-build docs
  • Study (45 min): What does the generated Rust code look like? How does prost handle oneof, repeated, and nested messages?
  • Practice (45 min): Set up build.rs that calls tonic_build::compile_protos; use the generated types for serialization/deserialization
  • Challenge (30 min): Add a google.protobuf.Timestamp field to a message; implement From<chrono::DateTime<Utc>> for the generated type
← Week 1: Protocol Buffers & gRPC

Project Setup

# Cargo.toml
[dependencies]
prost = "0.13"
tonic = "0.12"

[build-dependencies]
tonic-build = "0.12"
// build.rs
fn main() -> Result<(), Box<dyn std::error::Error>> {
    tonic_build::configure()
        .build_server(true)
        .build_client(true)
        .compile_protos(
            &["proto/myapp/v1/user.proto"],
            &["proto"],
        )?;
    Ok(())
}
← Week 1: Protocol Buffers & gRPC

Using Generated Types

// Include generated code
pub mod myapp {
    pub mod v1 {
        tonic::include_proto!("myapp.v1");
    }
}

use myapp::v1::{User, UserStatus};
use prost::Message;

// Create
let user = User {
    id: 42,
    name: "Alice".to_string(),
    email: "alice@example.com".to_string(),
    status: UserStatus::Active as i32,
    ..Default::default()
};

// Serialize to bytes
let mut buf = Vec::new();
user.encode(&mut buf).unwrap();

// Deserialize
let decoded = User::decode(buf.as_slice()).unwrap();
assert_eq!(decoded.name, "Alice");
← Week 1: Protocol Buffers & gRPC

Handling Well-Known Types

// proto/user.proto
import "google/protobuf/timestamp.proto";
message User {
  google.protobuf.Timestamp created_at = 8;
}
use prost_types::Timestamp;
use chrono::{DateTime, Utc};

impl From<DateTime<Utc>> for Timestamp {
    fn from(dt: DateTime<Utc>) -> Self {
        Timestamp {
            seconds: dt.timestamp(),
            nanos: dt.timestamp_subsec_nanos() as i32,
        }
    }
}
← Week 1: Protocol Buffers & gRPC

Organizing Proto Files

Best practice: mirror package path in directory structure:

proto/
  myapp/
    v1/
      user.proto
      service.proto
    v2/
      user.proto  (backward compatible evolution)

Version in the package name (myapp.v1) — when you need breaking changes, create myapp.v2 with new types and run both versions in parallel during migration.

← Week 1: Protocol Buffers & gRPC

Key Takeaways

  • tonic-build in build.rs generates both client and server stubs from .proto
  • prost::Message provides encode() and decode() for any generated type
  • include_proto! embeds generated code into your module tree
  • Organize protos by package + version; use reserved fields to prevent number reuse

Tomorrow: gRPC concepts — service types, streaming, and why it's faster than REST.