Ilya Brin - Software Engineer

History is written by its contributors

HTTP Server: Go vs Rust

2026-05-04 2 min read Backend Ilya Brin

Let’s compare how a basic HTTP server looks in Go and Rust — two languages often mentioned together when performance and reliability matter.

A Minimal HTTP Server

Goal: listen on port 8080, respond with Hello, World! for any GET request to /.

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, World!")
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}
use std::io::{BufRead, BufReader, Write};
use std::net::{TcpListener, TcpStream};

fn handle(mut stream: TcpStream) {
    let reader = BufReader::new(&stream);
    for line in reader.lines() {
        if line.unwrap_or_default().is_empty() { break; }
    }
    let response = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, World!";
    stream.write_all(response.as_bytes()).unwrap();
}

fn main() {
    let listener = TcpListener::bind("0.0.0.0:8080").unwrap();
    for stream in listener.incoming().flatten() {
        handle(stream);
    }
}

Go’s stdlib ships a full HTTP server with routing. In Rust, the standard library only gives you TCP — in production you’d use Axum or Actix Web.

Routing and JSON

Add a /ping route that returns JSON.

package main

import (
    "encoding/json"
    "net/http"
)

type Pong struct {
    Status string `json:"status"`
}

func pingHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(Pong{Status: "ok"})
}

func main() {
    http.HandleFunc("/ping", pingHandler)
    http.ListenAndServe(":8080", nil)
}
use axum::{routing::get, Router};
use serde::Serialize;
use axum::Json;

#[derive(Serialize)]
struct Pong {
    status: &'static str,
}

async fn ping() -> Json<Pong> {
    Json(Pong { status: "ok" })
}

#[tokio::main]
async fn main() {
    let app = Router::new().route("/ping", get(ping));
    let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

Which to Pick?

GoRust
Learning curveLowHigh
Built-in HTTP libYesTCP only
Async modelGoroutinesTokio/async-std
Compile speedFastSlow
Runtime overheadGCNone

Go is the pragmatic choice for most backend services. Rust gives you maximum control when every millisecond and every byte counts.

comments powered by Disqus