Go Standard Library

Go has a rich standard library. Here are the packages you'll use most often.

fmt - Formatted I/O

import "fmt"

// Print functions
fmt.Print("Hello")           // No newline
fmt.Println("Hello")         // With newline
fmt.Printf("Name: %s\n", n)  // Formatted

// Sprint functions (return string)
s := fmt.Sprint("Hello")
s := fmt.Sprintf("Name: %s", name)

// Fprint functions (write to io.Writer)
fmt.Fprint(os.Stderr, "Error!")
fmt.Fprintf(w, "Status: %d", code)

// Scanning
var name string
var age int
fmt.Scan(&name)                    // Read word
fmt.Scanf("%s %d", &name, &age)    // Formatted
fmt.Scanln(&name)                  // Read line

strings - String Manipulation

import "strings"

s := "Hello, World!"

strings.Contains(s, "World")        // true
strings.HasPrefix(s, "Hello")       // true
strings.HasSuffix(s, "!")           // true
strings.Index(s, "o")               // 4
strings.LastIndex(s, "o")           // 8
strings.Count(s, "l")               // 3

strings.ToUpper(s)                  // "HELLO, WORLD!"
strings.ToLower(s)                  // "hello, world!"
strings.Title(s)                    // "Hello, World!" (deprecated, use cases.Title)

strings.TrimSpace("  hi  ")         // "hi"
strings.Trim("!!hi!!", "!")         // "hi"
strings.TrimPrefix("hello", "he")   // "llo"
strings.TrimSuffix("hello", "lo")   // "hel"

strings.Replace(s, "World", "Go", 1)  // "Hello, Go!"
strings.ReplaceAll(s, "l", "L")       // "HeLLo, WorLd!"

strings.Split("a,b,c", ",")         // []string{"a", "b", "c"}
strings.Join([]string{"a","b"}, "-") // "a-b"

strings.Repeat("ab", 3)             // "ababab"

// Builder for efficient concatenation
var b strings.Builder
b.WriteString("Hello")
b.WriteString(", ")
b.WriteString("World!")
result := b.String()  // "Hello, World!"

strconv - String Conversions

import "strconv"

// String to number
i, err := strconv.Atoi("42")              // int
i64, err := strconv.ParseInt("42", 10, 64)  // int64
f, err := strconv.ParseFloat("3.14", 64)    // float64
b, err := strconv.ParseBool("true")         // bool

// Number to string
s := strconv.Itoa(42)                       // "42"
s := strconv.FormatInt(42, 10)              // "42"
s := strconv.FormatFloat(3.14, 'f', 2, 64)  // "3.14"
s := strconv.FormatBool(true)               // "true"

// Quote/Unquote
s := strconv.Quote("hello\nworld")          // "\"hello\\nworld\""
s, _ := strconv.Unquote("\"hello\"")        // "hello"

io - Basic I/O

import "io"

// Core interfaces
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// Common functions
n, err := io.Copy(dst, src)           // Copy all data
n, err := io.CopyN(dst, src, 1024)    // Copy n bytes
data, err := io.ReadAll(r)            // Read everything

// Utilities
r := io.LimitReader(src, 1024)        // Limit reading
r := io.TeeReader(src, dst)           // Read and copy
w := io.MultiWriter(w1, w2)           // Write to multiple
r := io.MultiReader(r1, r2)           // Read from multiple

// Discard
io.Copy(io.Discard, r)                // Read and discard

os - Operating System

import "os"

// Environment
os.Getenv("HOME")
os.Setenv("KEY", "value")
os.Environ()  // []string{"KEY=value", ...}

// Files
f, err := os.Open("file.txt")           // Read-only
f, err := os.Create("file.txt")         // Create/truncate
f, err := os.OpenFile("file.txt", os.O_RDWR|os.O_CREATE, 0644)

data, err := os.ReadFile("file.txt")    // Read entire file
err := os.WriteFile("file.txt", data, 0644)  // Write entire file

// File operations
err := os.Remove("file.txt")
err := os.RemoveAll("dir")
err := os.Rename("old.txt", "new.txt")
err := os.Mkdir("dir", 0755)
err := os.MkdirAll("path/to/dir", 0755)

info, err := os.Stat("file.txt")
info.Name()     // filename
info.Size()     // size in bytes
info.IsDir()    // is directory?
info.ModTime()  // modification time

// Working directory
cwd, err := os.Getwd()
err := os.Chdir("/path")

// Process
os.Exit(1)            // Exit with code
os.Args               // Command-line arguments
os.Stdin, os.Stdout, os.Stderr  // Standard streams

path/filepath - File Paths

import "path/filepath"

// Path manipulation (OS-aware)
filepath.Join("dir", "file.txt")     // "dir/file.txt" or "dir\file.txt"
filepath.Dir("/path/to/file.txt")    // "/path/to"
filepath.Base("/path/to/file.txt")   // "file.txt"
filepath.Ext("file.txt")             // ".txt"

filepath.Abs("relative/path")        // Absolute path
filepath.Rel("/base", "/base/path")  // "path"
filepath.Clean("path/../other")      // "other"

// Walking directories
filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
    if err != nil {
        return err
    }
    fmt.Println(path)
    return nil
})

// WalkDir (Go 1.16+, more efficient)
filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
    if err != nil {
        return err
    }
    fmt.Println(path)
    return nil
})

// Globbing
matches, err := filepath.Glob("*.go")

time - Time and Duration

import "time"

// Current time
now := time.Now()
now.Year(), now.Month(), now.Day()
now.Hour(), now.Minute(), now.Second()
now.Weekday()  // time.Sunday, etc.
now.Unix()     // Unix timestamp

// Creating times
t := time.Date(2024, time.January, 1, 12, 0, 0, 0, time.UTC)

// Parsing
t, err := time.Parse("2006-01-02", "2024-01-15")
t, err := time.Parse(time.RFC3339, "2024-01-15T12:00:00Z")

// Formatting (use reference time: Mon Jan 2 15:04:05 MST 2006)
t.Format("2006-01-02")           // "2024-01-15"
t.Format("Jan 2, 2006")          // "Jan 15, 2024"
t.Format(time.RFC3339)           // "2024-01-15T12:00:00Z"
t.Format("3:04 PM")              // "12:00 PM"

// Duration
d := 5 * time.Second
d := time.Hour + 30*time.Minute
d := time.Duration(n) * time.Millisecond

// Arithmetic
later := now.Add(24 * time.Hour)
earlier := now.Add(-1 * time.Hour)
diff := t2.Sub(t1)  // Duration

// Comparison
t1.Before(t2)
t1.After(t2)
t1.Equal(t2)

// Sleeping and timers
time.Sleep(time.Second)

timer := time.NewTimer(time.Second)
<-timer.C  // Blocks until timer fires
timer.Stop()

ticker := time.NewTicker(time.Second)
for t := range ticker.C {
    fmt.Println("Tick at", t)
}
ticker.Stop()

// Timeout
select {
case result := <-ch:
    // Got result
case <-time.After(5 * time.Second):
    // Timeout
}

encoding/json - JSON

import "encoding/json"

type User struct {
    Name  string `json:"name"`
    Email string `json:"email,omitempty"`
    Age   int    `json:"age"`
}

// Marshal (struct to JSON)
user := User{Name: "Alice", Age: 30}
data, err := json.Marshal(user)
// {"name":"Alice","age":30}

// Pretty print
data, err := json.MarshalIndent(user, "", "  ")

// Unmarshal (JSON to struct)
var user User
err := json.Unmarshal(data, &user)

// Streaming (for large data)
// Encoder
encoder := json.NewEncoder(w)
encoder.Encode(user)

// Decoder
decoder := json.NewDecoder(r)
decoder.Decode(&user)

// Working with unknown JSON
var result map[string]interface{}
json.Unmarshal(data, &result)

// Or with any
var result any
json.Unmarshal(data, &result)

net/http - HTTP Client & Server

HTTP Client

import "net/http"

// Simple GET
resp, err := http.Get("https://api.example.com/data")
if err != nil {
    return err
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)

// Simple POST
resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData))

// Custom request
req, err := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer token")

client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)

// With context
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := http.DefaultClient.Do(req)

HTTP Server

import "net/http"

// Simple handler
func helloHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/plain")
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("Hello, World!"))
}

// JSON handler
func jsonHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]string{"message": "hello"})
}

// Register handlers
http.HandleFunc("/hello", helloHandler)
http.HandleFunc("/api/data", jsonHandler)

// Start server
http.ListenAndServe(":8080", nil)

// With custom mux
mux := http.NewServeMux()
mux.HandleFunc("/", homeHandler)
mux.HandleFunc("/api/", apiHandler)

server := &http.Server{
    Addr:         ":8080",
    Handler:      mux,
    ReadTimeout:  10 * time.Second,
    WriteTimeout: 10 * time.Second,
}
server.ListenAndServe()

log - Logging

import "log"

// Basic logging
log.Print("Info message")
log.Printf("User: %s", name)
log.Println("With newline")

// Fatal (logs and exits)
log.Fatal("Critical error")
log.Fatalf("Error: %v", err)

// Panic (logs and panics)
log.Panic("Unrecoverable error")

// Custom logger
logger := log.New(os.Stdout, "APP: ", log.Ldate|log.Ltime|log.Lshortfile)
logger.Println("Custom log")

// Flags
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
// log.Ldate        // 2009/01/23
// log.Ltime        // 01:23:23
// log.Lmicroseconds
// log.Llongfile    // /a/b/c/d.go:23
// log.Lshortfile   // d.go:23
// log.LUTC         // Use UTC
// log.Lmsgprefix   // Move prefix to before message

// Log to file
f, _ := os.OpenFile("app.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
log.SetOutput(f)

log/slog - Structured Logging (Go 1.21+)

import "log/slog"

// Basic usage
slog.Info("User logged in", "user_id", 123, "ip", "192.168.1.1")
slog.Warn("Rate limit approaching", "current", 95, "max", 100)
slog.Error("Failed to save", "error", err)

// With context
slog.InfoContext(ctx, "Processing request")

// Create custom logger
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
logger.Info("Structured log", "key", "value")

// With attributes
logger := slog.With("service", "api", "version", "1.0")
logger.Info("Request received")

// Log levels
slog.Debug("debug message")  // Only shown if level allows
slog.Info("info message")
slog.Warn("warning message")
slog.Error("error message")

regexp - Regular Expressions

import "regexp"

// Compile (panics on error)
re := regexp.MustCompile(`\d+`)

// Compile (returns error)
re, err := regexp.Compile(`\d+`)

// Matching
re.MatchString("abc123")         // true
re.FindString("abc123def")       // "123"
re.FindAllString("a1b2c3", -1)   // []string{"1", "2", "3"}

// With submatches
re := regexp.MustCompile(`(\w+)@(\w+)\.(\w+)`)
matches := re.FindStringSubmatch("user@example.com")
// []string{"user@example.com", "user", "example", "com"}

// Named groups
re := regexp.MustCompile(`(?P<user>\w+)@(?P<domain>\w+\.\w+)`)
matches := re.FindStringSubmatch("user@example.com")
names := re.SubexpNames()  // []string{"", "user", "domain"}

// Replace
re := regexp.MustCompile(`\s+`)
re.ReplaceAllString("a   b   c", " ")  // "a b c"

// Split
re := regexp.MustCompile(`[,;]`)
re.Split("a,b;c", -1)  // []string{"a", "b", "c"}

sort - Sorting

import "sort"

// Sort slices
nums := []int{3, 1, 4, 1, 5}
sort.Ints(nums)  // [1, 1, 3, 4, 5]

strs := []string{"banana", "apple", "cherry"}
sort.Strings(strs)  // [apple, banana, cherry]

// Reverse
sort.Sort(sort.Reverse(sort.IntSlice(nums)))

// Custom sorting
sort.Slice(people, func(i, j int) bool {
    return people[i].Age < people[j].Age
})

// Stable sort
sort.SliceStable(people, func(i, j int) bool {
    return people[i].Age < people[j].Age
})

// Check if sorted
sort.IntsAreSorted(nums)

// Binary search
i := sort.SearchInts(nums, 4)  // Index where 4 should be

sync - Synchronization

import "sync"

// WaitGroup
var wg sync.WaitGroup
wg.Add(1)
go func() {
    defer wg.Done()
    // work
}()
wg.Wait()

// Mutex
var mu sync.Mutex
mu.Lock()
defer mu.Unlock()

// RWMutex
var mu sync.RWMutex
mu.RLock()   // Multiple readers OK
mu.RUnlock()
mu.Lock()    // Exclusive write
mu.Unlock()

// Once
var once sync.Once
once.Do(func() {
    // Runs exactly once
})

// Map (concurrent-safe)
var m sync.Map
m.Store("key", "value")
value, ok := m.Load("key")
m.Delete("key")
m.Range(func(key, value interface{}) bool {
    fmt.Println(key, value)
    return true  // Continue iteration
})

// Pool
var pool = sync.Pool{
    New: func() interface{} {
        return new(bytes.Buffer)
    },
}
buf := pool.Get().(*bytes.Buffer)
defer pool.Put(buf)

context - Request Scoping

import "context"

// Background context (top-level)
ctx := context.Background()

// TODO context (when unsure)
ctx := context.TODO()

// With cancellation
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// With timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

// With deadline
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Hour))
defer cancel()

// With value (use sparingly)
ctx := context.WithValue(parentCtx, "userID", 123)
userID := ctx.Value("userID").(int)

// Check for cancellation
select {
case <-ctx.Done():
    return ctx.Err()  // context.Canceled or context.DeadlineExceeded
default:
    // Continue
}

Next Steps

Continue to 13-best-practices.md to learn about Go idioms and best practices.