CODE · Last verified April 2026
Rust vs Go side-by-side:
the same task in both languages.
Real, runnable code. No pseudo-code. Each sample has an honest annotation about what the language does well and where it struggles.
DEEP DIVE
HTTP server: full code, both languages, TechEmpower numbers
Error handling
Propagating errors up the call stack.
RUST
use std::fs;
use std::num::ParseIntError;
fn read_and_parse(path: &str) -> Result<i32, Box<dyn std::error::Error>> {
let content = fs::read_to_string(path)?; // ? propagates error
let num: i32 = content.trim().parse()?; // ? propagates ParseIntError
Ok(num * 2)
}
fn main() {
match read_and_parse("number.txt") {
Ok(n) => println!("Result: {n}"),
Err(e) => eprintln!("Error: {e}"),
}
}Rust: The ? operator propagates errors up automatically. Rust forces you to handle errors at compile time.
GO
package main
import (
"fmt"
"os"
"strconv"
"strings"
)
func readAndParse(path string) (int, error) {
data, err := os.ReadFile(path)
if err != nil {
return 0, err // must handle every error explicitly
}
n, err := strconv.Atoi(strings.TrimSpace(string(data)))
if err != nil {
return 0, err
}
return n * 2, nil
}
func main() {
n, err := readAndParse("number.txt")
if err != nil {
fmt.Fprintln(os.Stderr, "Error:", err)
return
}
fmt.Println("Result:", n)
}Go: if err != nil repeated for every fallible call. Verbose but explicit. No silent failures.
JSON parsing
Read a JSON file, parse it, access a field.
RUST
use serde::{Deserialize, Serialize};
use std::fs;
#[derive(Deserialize, Serialize, Debug)]
struct User {
name: String,
age: u32,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let content = fs::read_to_string("user.json")?;
let user: User = serde_json::from_str(&content)?;
println!("Name: {}, Age: {}", user.name, user.age);
Ok(())
}Rust: serde is the de-facto Rust JSON library. Derive macros handle serialization/deserialization with zero boilerplate at runtime.
GO
package main
import (
"encoding/json"
"fmt"
"os"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
data, err := os.ReadFile("user.json")
if err != nil {
panic(err)
}
var user User
if err := json.Unmarshal(data, &user); err != nil {
panic(err)
}
fmt.Printf("Name: %s, Age: %d\n", user.Name, user.Age)
}Go: encoding/json is stdlib. Struct tags (json:'name') map JSON keys. No external dependency needed.
Channel producer/consumer
Fan-out work across workers, collect results.
RUST
use tokio::sync::mpsc;
#[tokio::main]
async fn main() {
let (tx, mut rx) = mpsc::channel::<i32>(32);
for i in 0..5 {
let tx = tx.clone();
tokio::spawn(async move {
tx.send(i * i).await.unwrap();
});
}
drop(tx); // close sender so receiver drains
let mut results = vec![];
while let Some(val) = rx.recv().await {
results.push(val);
}
println!("{results:?}");
}Rust: Tokio's mpsc channel. drop(tx) closes the channel. Works naturally with async/await.
GO
package main
import (
"fmt"
"sync"
)
func main() {
ch := make(chan int, 32)
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
ch <- n * n
}(i)
}
// close channel after all goroutines finish
go func() {
wg.Wait()
close(ch)
}()
var results []int
for v := range ch {
results = append(results, v)
}
fmt.Println(results)
}Go: Channels are first-class in Go. close() signals completion. for range drains naturally.