Frontend Labs
⋯

Day 22 — Generics (Go 1.18+)

Learning Objectives

  • Write generic data structures used across your entire security proxy
  • Build a RingBuffer[T] used for metrics, log entries, and connection histories
  • Understand constraints: when comparable vs any

Key Concepts

// Generic function — works for any ordered numeric type
func Min[T int | float64 | int64](a, b T) T {
    if a < b { return a }
    return b
}

// Generic type — RingBuffer[T] works for any T
type RingBuffer[T any] struct {
    items []T
    head  int
    count int
    size  int
}

// Type constraints
type Number interface {
    int | int8 | int16 | int32 | int64 | float32 | float64
}

// comparable — for map keys and equality checks
func Contains[T comparable](slice []T, item T) bool {
    for _, s := range slice {
        if s == item { return true }
    }
    return false
}

Exercise: Generic RingBuffer for the Security Stack

package main

import "fmt"

// RingBuffer is a fixed-size circular buffer — O(1) add, O(n) scan
// Used for: last-N request log, latency histogram, connection history
type RingBuffer[T any] struct {
    items []T
    head  int
    count int
    size  int
}

func NewRingBuffer[T any](size int) *RingBuffer[T] {
    return &RingBuffer[T]{items: make([]T, size), size: size}
}

func (rb *RingBuffer[T]) Add(item T) {
    rb.items[rb.head] = item
    rb.head = (rb.head + 1) % rb.size
    if rb.count < rb.size {
        rb.count++
    }
}

func (rb *RingBuffer[T]) All() []T {
    result := make([]T, rb.count)
    for i := 0; i < rb.count; i++ {
        result[i] = rb.items[(rb.head-rb.count+i+rb.size)%rb.size]
    }
    return result
}

func (rb *RingBuffer[T]) Len() int { return rb.count }

// Result[T] — type-safe wrapper for (value, error)
type Result[T any] struct {
    Value T
    Err   error
}

func OK[T any](v T) Result[T]     { return Result[T]{Value: v} }
func Err[T any](e error) Result[T] { return Result[T]{Err: e} }

func (r Result[T]) Unwrap() (T, error) { return r.Value, r.Err }

func main() {
    // Latency ring buffer (last 1000 request latencies for p99 calc)
    latencies := NewRingBuffer[int64](1000)
    for _, ms := range []int64{5, 12, 8, 45, 3, 100, 7, 23} {
        latencies.Add(ms)
    }
    fmt.Printf("Last %d latencies: %v\n", latencies.Len(), latencies.All())

    // Log entry ring buffer
    type LogEntry struct{ IP, Path string; Status int }
    logs := NewRingBuffer[LogEntry](100)
    logs.Add(LogEntry{"10.0.0.1", "/api/users", 200})
    logs.Add(LogEntry{"10.0.0.2", "/api/admin", 403})
    for _, e := range logs.All() {
        fmt.Printf("%s %s %d\n", e.IP, e.Path, e.Status)
    }

    // Result type for auth operations
    authResult := OK("user:42")
    if user, err := authResult.Unwrap(); err == nil {
        fmt.Println("authenticated:", user)
    }
}

Real-World Context

The RingBuffer[int64] stores the last 1000 request latencies. A simple sort of .All() gives you p50, p95, p99. This is your home-grown metrics system — no Prometheus library needed. You'll use it in the Week 4 mini project and connect it to the observability layer in Week 9.


Generated from week4.md · 8 Oct 2026 16:36