Polyglot
Languages Go stdlib
Go § stdlib

Standard library

The Go standard library covers a substantial portion of routine programming: formatted I/O, string manipulation, file system, networking, HTTP, JSON, time, regular expressions, cryptography, compression, database access (via the SQL driver interface), and more. The library is intentionally broad and stable — packages added to the standard library remain available indefinitely. The conventional Go program leans on the standard library; third-party crates are common (for routing, database ORMs, observability tooling) but the core surface is built in. The combination — substantial breadth, deep stability, single-source documentation at pkg.go.dev — is the substance of Go’s standard-library story.

This tour points out the principal packages and their conventional uses.

fmt

Formatted I/O — the conventional print and format functions:

import "fmt"

fmt.Println("hello", "world")                     // print with newline
fmt.Printf("%s is %d\n", name, age)              // formatted print
fmt.Print("no newline ")
fmt.Sprintln(...)                                 // returns string
fmt.Sprintf(...)
fmt.Sprint(...)

// To io.Writer:
fmt.Fprintln(os.Stderr, "error:", err)
fmt.Fprintf(file, "%d\n", n)

// Reading:
var n int
fmt.Scan(&n)                                      // from stdin
fmt.Sscanf("42", "%d", &n)

The format verbs are treated in Strings.

strings and strconv

strings for manipulation; strconv for numeric conversions:

import (
    "strings"
    "strconv"
)

strings.Contains("hello", "ll")
strings.HasPrefix("hello", "he")
strings.Split("a,b,c", ",")
strings.Join([]string{"a", "b"}, ",")
strings.ToUpper("hello")
strings.TrimSpace("  hello  ")
strings.Replace("hello", "l", "L", -1)            // -1 = all

strconv.Itoa(42)
strconv.Atoi("42")
strconv.FormatFloat(3.14, 'f', 2, 64)
strconv.ParseFloat("3.14", 64)

Treated in Strings.

time

Time and duration:

import "time"

now := time.Now()
fmt.Println(now)
fmt.Println(now.Format(time.RFC3339))            // "2026-05-07T15:04:05Z"
fmt.Println(now.Format("2006-01-02 15:04:05"))   // Go's reference time

t, err := time.Parse(time.RFC3339, "2026-01-15T10:00:00Z")
t, err := time.Parse("2006-01-02", "2026-01-15")

d := 5 * time.Second
d := 100 * time.Millisecond
d := time.Hour + 30*time.Minute

later := now.Add(d)
diff := later.Sub(now)                           // Duration

time.Sleep(2 * time.Second)

start := time.Now()
doWork()
elapsed := time.Since(start)

The reference time is Mon Jan 2 15:04:05 MST 2006 — the format strings spell out fields using these exact values. The convention is unique to Go.

For durations:

time.Nanosecond
time.Microsecond
time.Millisecond
time.Second
time.Minute
time.Hour

// Methods:
d.Seconds()                                       // float64
d.Milliseconds()                                  // int64
d.Hours()

For tickers and timers:

ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for t := range ticker.C {
    fmt.Println(t)
}

timer := time.NewTimer(5 * time.Second)
<-timer.C                                         // blocks for 5 seconds

os

Operating-system interaction:

import "os"

os.Args                                           // []string
os.Getenv("HOME")
os.Setenv("KEY", "value")
os.Environ()                                      // []string ("KEY=VALUE" pairs)

dir, err := os.Getwd()                            // current directory
err := os.Chdir("/tmp")
err := os.Mkdir("dir", 0755)
err := os.MkdirAll("a/b/c", 0755)
err := os.Remove("file.txt")
err := os.RemoveAll("dir")

f, err := os.Open("file.txt")
f, err := os.Create("output.txt")
defer f.Close()

info, err := os.Stat("file.txt")
fmt.Println(info.Size(), info.ModTime())

os.Exit(1)                                        // exit with status

// stdin/stdout/stderr:
os.Stdin                                          // *os.File
os.Stdout
os.Stderr

path/filepath

Cross-platform path manipulation:

import "path/filepath"

filepath.Join("a", "b", "c")                     // "a/b/c" or "a\\b\\c"
filepath.Base("/etc/hosts")                      // "hosts"
filepath.Dir("/etc/hosts")                        // "/etc"
filepath.Ext("file.txt")                         // ".txt"
filepath.Abs("./relative")                        // absolute path
filepath.Clean("a/./b/../c")                      // "a/c"

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

filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
    fmt.Println(path)
    return nil
})

// Or with the Go 1.16+ fs.WalkDir for better performance:
filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
    return nil
})

For URL-style paths (always /-separated), use path instead of path/filepath.

io

The principal I/O interfaces:

import "io"

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

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

type Closer interface {
    Close() error
}

// Combined interfaces:
io.ReadWriter
io.ReadCloser
io.WriteCloser
io.ReadWriteCloser
io.Seeker

Conventional functions:

io.Copy(dst, src)                                 // copy until EOF
io.CopyN(dst, src, n)                             // copy n bytes
io.ReadAll(r)                                     // []byte (Go 1.16+)
io.ReadFull(r, buf)                               // fill buf or error

io.LimitReader(r, n)                              // bounded reader
io.MultiReader(r1, r2, r3)                        // concatenated readers
io.MultiWriter(w1, w2)                            // tee writer
io.TeeReader(r, w)                                // reads from r and writes to w

io.EOF                                            // sentinel error for end of stream

Treated in I/O.

bufio

Buffered I/O:

import "bufio"

reader := bufio.NewReader(os.Stdin)
line, err := reader.ReadString('\n')

scanner := bufio.NewScanner(file)
for scanner.Scan() {
    line := scanner.Text()
    process(line)
}

writer := bufio.NewWriter(file)
writer.WriteString("hello\n")
writer.Flush()

Treated in I/O.

encoding/json

JSON serialisation and deserialisation:

import "encoding/json"

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

// Marshal:
data, err := json.Marshal(p)
data, err := json.MarshalIndent(p, "", "  ")

// Unmarshal:
var p Person
err := json.Unmarshal(data, &p)

// Streaming:
encoder := json.NewEncoder(file)
encoder.SetIndent("", "  ")
encoder.Encode(p)

decoder := json.NewDecoder(file)
err := decoder.Decode(&p)

// Generic JSON:
var m map[string]interface{}
err := json.Unmarshal(data, &m)

Other encoding packages: encoding/xml, encoding/csv, encoding/base64, encoding/hex, encoding/binary.

net/http

HTTP client and server:

import "net/http"

// Client:
resp, err := http.Get("https://example.com")
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)

resp, err := http.Post(url, "application/json", strings.NewReader(body))

client := &http.Client{
    Timeout: 10 * time.Second,
}
req, err := http.NewRequest("POST", url, body)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)

// Server:
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, world!")
})

http.HandleFunc("/api/", apiHandler)

log.Fatal(http.ListenAndServe(":8080", nil))

// With a custom router (since Go 1.22):
mux := http.NewServeMux()
mux.HandleFunc("GET /users/{id}", getUser)
mux.HandleFunc("POST /users", createUser)
log.Fatal(http.ListenAndServe(":8080", mux))

The Go 1.22 net/http router admits substantial path matching with method specifiers and path variables.

net

Lower-level networking:

import "net"

// TCP:
conn, err := net.Dial("tcp", "example.com:80")
listener, err := net.Listen("tcp", ":8080")

for {
    conn, err := listener.Accept()
    go handle(conn)
}

// UDP:
addr, err := net.ResolveUDPAddr("udp", ":8080")
conn, err := net.ListenUDP("udp", addr)

// DNS:
ips, err := net.LookupIP("example.com")
mxs, err := net.LookupMX("example.com")

context

Cancellation and deadline propagation:

import "context"

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

ctx, cancel := context.WithCancel(parent)
ctx := context.WithValue(parent, key, value)

select {
case <-ctx.Done():
    return ctx.Err()
case result := <-doWork(ctx):
    return result, nil
}

Treated in Concurrency.

log and log/slog

Logging:

import "log"

log.Println("info message")
log.Printf("count: %d", n)
log.Fatal("fatal — exits with status 1")
log.Panic("logs and panics")

// Custom logger:
logger := log.New(os.Stderr, "PREFIX ", log.LstdFlags|log.Lshortfile)
logger.Println("hello")

Since Go 1.21, log/slog provides structured logging:

import "log/slog"

slog.Info("user logged in", "user", username, "ip", ip)
slog.Error("operation failed", "error", err, "id", id)

logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
logger.Info("structured", "key", "value")

The slog package is the conventional contemporary Go logger.

regexp

Regular expressions:

import "regexp"

re := regexp.MustCompile(`^[a-zA-Z]+$`)
re.MatchString("hello")                           // true
re.FindString("hello world")                      // "hello"
re.FindAllString("a1b2c3", -1)                    // ["a", "b", "c"]
re.ReplaceAllString("a1b2", "X")                  // "X1X2"

re := regexp.MustCompile(`(\w+) (\w+)`)
matches := re.FindStringSubmatch("hello world")   // ["hello world", "hello", "world"]

Go’s regexp uses RE2 syntax — guarantees linear time but does not support backreferences.

sort and slices

Sorting:

import "sort"

s := []int{3, 1, 4}
sort.Ints(s)
sort.Strings(strs)
sort.Sort(sort.Reverse(sort.IntSlice(s)))

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

idx := sort.Search(len(s), func(i int) bool { return s[i] >= target })

Since Go 1.21, the slices package provides generic sorting:

import "slices"

slices.Sort(s)
slices.SortFunc(people, func(a, b Person) int {
    return cmp.Compare(a.Age, b.Age)
})
slices.IsSorted(s)
slices.BinarySearch(s, target)

database/sql

Database access:

import (
    "database/sql"
    _ "github.com/lib/pq"                         // driver registration
)

db, err := sql.Open("postgres", connStr)
defer db.Close()

err := db.Ping()

rows, err := db.Query("SELECT id, name FROM users WHERE active = $1", true)
defer rows.Close()
for rows.Next() {
    var id int
    var name string
    if err := rows.Scan(&id, &name); err != nil { /* ... */ }
    fmt.Println(id, name)
}

err := db.QueryRow("SELECT name FROM users WHERE id = $1", id).Scan(&name)

result, err := db.Exec("UPDATE users SET active = $1 WHERE id = $2", true, id)
n, err := result.RowsAffected()

// Transactions:
tx, err := db.Begin()
defer tx.Rollback()
tx.Exec(...)
tx.Commit()

The package admits a driver-independent interface; the database driver (PostgreSQL, MySQL, SQLite) is imported separately.

crypto/...

Cryptography:

import (
    "crypto/sha256"
    "crypto/hmac"
    "crypto/rand"
    "crypto/aes"
    "crypto/tls"
)

h := sha256.Sum256([]byte("hello"))               // [32]byte
fmt.Printf("%x\n", h)

mac := hmac.New(sha256.New, key)
mac.Write(message)
sig := mac.Sum(nil)

buf := make([]byte, 16)
rand.Read(buf)                                    // cryptographically secure random

encoding/...

Encoding utilities:

import (
    "encoding/json"
    "encoding/xml"
    "encoding/csv"
    "encoding/base64"
    "encoding/hex"
    "encoding/binary"
)

base64.StdEncoding.EncodeToString(data)
base64.StdEncoding.DecodeString(s)

hex.EncodeToString(data)

binary.BigEndian.Uint32(buf[0:4])
binary.LittleEndian.PutUint32(buf, value)

compress/...

Compression:

import (
    "compress/gzip"
    "compress/zlib"
)

w := gzip.NewWriter(file)
w.Write(data)
w.Close()

r, _ := gzip.NewReader(file)
defer r.Close()
data, _ := io.ReadAll(r)

errors

Error utilities:

import "errors"

err := errors.New("something failed")
errors.Is(err, target)
errors.As(err, &targetType)
errors.Unwrap(err)
errors.Join(err1, err2, err3)                     // Go 1.20+

Treated in Error handling.

flag

Command-line argument parsing:

import "flag"

var (
    name = flag.String("name", "world", "name to greet")
    age  = flag.Int("age", 0, "age")
    verbose = flag.Bool("v", false, "verbose")
)

flag.Parse()

fmt.Printf("Hello, %s (%d)\n", *name, *age)

For more elaborate CLIs, third-party crates like cobra and urfave/cli are conventional.

testing

Test framework:

import "testing"

func TestAdd(t *testing.T) {
    if got := Add(2, 3); got != 5 {
        t.Errorf("Add(2, 3) = %d, want 5", got)
    }
}

func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(2, 3)
    }
}

func ExampleAdd() {
    fmt.Println(Add(2, 3))
    // Output: 5
}

// Table-driven tests:
func TestParse(t *testing.T) {
    tests := []struct {
        in   string
        want int
        err  bool
    }{
        {"42", 42, false},
        {"abc", 0, true},
    }

    for _, tc := range tests {
        t.Run(tc.in, func(t *testing.T) {
            got, err := Parse(tc.in)
            if (err != nil) != tc.err {
                t.Errorf("err = %v", err)
            }
            if got != tc.want {
                t.Errorf("got %d, want %d", got, tc.want)
            }
        })
    }
}

Run with go test, go test -v (verbose), go test -race (race detection), go test -cover (coverage).

runtime

Runtime information:

import "runtime"

runtime.GOMAXPROCS(0)                             // number of OS threads
runtime.NumCPU()                                  // CPU cores
runtime.NumGoroutine()                            // active goroutines

var stats runtime.MemStats
runtime.ReadMemStats(&stats)

runtime.GC()                                      // force GC

runtime.Gosched()                                 // yield to other goroutines

sync and sync/atomic

Synchronisation primitives — treated in Concurrency.

A note on the conventional discipline

The Go standard library is the conventional starting point. Reach for third-party crates when:

  • Web routinggorilla/mux, chi, gin, echo.
  • Database ORMgorm, sqlx, sqlc, ent.
  • Configurationviper, koanf.
  • CLIcobra, urfave/cli.
  • Observabilityopentelemetry, prometheus.
  • gRPCgoogle.golang.org/grpc.
  • Async work queuesasynq, machinery.

The conventional Go ecosystem leans heavily on the standard library; many production applications use only standard packages plus a handful of third-party crates.

A note on the conventional discipline

The standard-library advice:

  • Reach for the standard library first — it covers most needs.
  • Use fmt, strings, strconv freely — they are the foundation.
  • Use time for time and duration; the reference format is unique.
  • Use os and path/filepath for OS interaction.
  • Use io and bufio — the Reader/Writer interfaces are foundational.
  • Use encoding/json for JSON; database/sql for SQL.
  • Use net/http — the substantial built-in HTTP support is one of Go’s strengths.
  • Use log/slog (Go 1.21+) for structured logging.
  • Use slices and maps (Go 1.21+) for the conventional generic operations.
  • Use testing — the standard testing framework is conventional.

The combination — substantial breadth, deep stability, the pkg.go.dev documentation, the go doc command, the conventional cmd/ and internal/ layout — is the substance of Go’s standard library philosophy. The discipline produces self-contained programs with substantial functionality.