Two years ago we rewrote a Go microservice in Rust. Here’s what we learned—the good, the ugly, and the surprising.
The Numbers
Before we get into opinions, here are the cold hard metrics comparing the Go (1.21) and Rust (1.78) versions processing 10M API requests:
| Metric | Go | Rust | Delta |
|---|---|---|---|
| P99 latency | 340ms | 28ms | -92% |
| Memory (steady state) | 2.1 GB | 48 MB | -98% |
| CPU (avg) | 4.2 cores | 0.8 cores | -81% |
| Binary size | 12 MB | 4.8 MB | -60% |
| Lines of code | 4,200 | 5,800 | +38% |
The latency drop isn’t just about Rust being faster—it’s about tail latency predictability. Go’s GC pauses, while short, created sporadic spikes that triggered downstream timeouts.
Where Rust Excelled
Zero-Cost Abstractions with Serde
#[derive(Deserialize, Serialize)]
struct EventPayload {
event_type: EventType,
#[serde(rename = "timestamp_ms")]
timestamp: i64,
#[serde(default)]
metadata: HashMap<String, Value>,
}
No reflection, no runtime type inspection. The compiler generates specialized serialize/deserialize code for each type. In benchmarks, this was ~4x faster than Go’s encoding/json with struct tags.
Error Handling That Doesn’t Hide
Go’s if err != nil pattern works, but Rust’s Result<T, E> with ? operator combined with thiserror and anyhow crates creates a system where:
- Errors are visible in function signatures
- Stack traces are preserved without manual wrapping
- Pattern matching on error variants is exhaustive
Where It Hurt
Async Ecosystem Fragmentation
Rust’s async story is powerful but fractured. Choosing between tokio vs async-std, hyper vs reqwest, and understanding Send + Sync bounds on futures took a month of experimentation.
The Learning Cliff
New team members took 6-8 weeks to become productive in Rust versus 1-2 weeks for Go. The borrow checker teaches valuable lessons, but at a cost.
Bottom Line
For latency-sensitive, resource-constrained services handling high throughput—Rust is the clear winner. For internal tooling, CRUD APIs, and rapid prototyping—Go still wins on developer velocity.
We’re not rewriting everything. We’re being surgical.