V
Vel·ToolKit
Simple · Fast · Ready to use
EN
Chapter 17 of 20

Standard Library

fmt / strings / time / json / context / sort / slog and more

Go's standard library covers networking, IO, text, crypto, compression, concurrency, templates — most everyday needs. Below is a tour of the packages used most in practice; mastering them lets 80% of your features avoid third-party libraries.

fmt: Formatted I/O

Printf / Sprintf / Fprintf / Errorf share one placeholder system. Common verbs:

%v   default format
%+v  struct with field names
%#v  Go-syntax literal
%T   type
%d   decimal integer
%b %o %x %X  binary/octal/hex
%f %e %g     floating point
%.2f         2 decimal places
%s   string
%q   quoted string (auto-escaped)
%c   rune character
%U   Unicode code point (U+4E2D)
%t   boolean
%p   pointer address
%w   wrap an error (fmt.Errorf only)
Width & alignment: %-8s left-align in 8 chars; %8d right-align in 8 chars
package main

import (
    "errors"
    "fmt"
)

func main() {
    name, age, score := "Alice", 30, 95.5
    fmt.Printf("%-8s %3d %.2f\n", name, age, score)

    s := fmt.Sprintf("hello %s", name)
    fmt.Println(s)

    orig := errors.New("disk full")
    err := fmt.Errorf("save %q: %w", "out.txt", orig) // wrap with %w
    fmt.Println(err)
    fmt.Println(errors.Is(err, orig)) // true
}

strings / bytes

strings operates on immutable strings; bytes provides an equivalent API over []byte. Their functions map almost one-to-one.

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "  Hello Go  "
    fmt.Println(strings.Contains(s, "Go"))                  // true
    fmt.Println(strings.HasPrefix("v1.22.0", "v1."))        // true
    fmt.Println(strings.Split("a,b,c", ","))                // [a b c]
    fmt.Println(strings.Join([]string{"a", "b"}, "/"))      // a/b
    fmt.Println(strings.ReplaceAll("a\r\nb", "\r\n", "\n")) // a\nb
    fmt.Println(strings.TrimSpace(s))                       // Hello Go
    fmt.Println(strings.Fields("  hello   go  "))           // [hello go]
    fmt.Println(strings.EqualFold("Go", "go"))              // true

    var b strings.Builder
    for i := 0; i < 100; i++ {
        b.WriteString("x")
    }
    fmt.Println(len(b.String())) // 100
}

strconv: String ⇄ Number

package main

import (
    "fmt"
    "strconv"
)

func main() {
    n, err := strconv.Atoi("42") // string -> int
    fmt.Println(n, err)

    s := strconv.Itoa(42) // int -> string
    fmt.Println(s)

    f, _ := strconv.ParseFloat("3.14", 64)        // string -> float64
    s2 := strconv.FormatFloat(3.14, 'f', 2, 64)    // "3.14"
    fmt.Println(f, s2)

    b, _ := strconv.ParseBool("true")
    fmt.Println(b)

    q := strconv.Quote(`he said "hi"`) // "he said \"hi\""
    fmt.Println(q)
}

time: Time and Duration

time.Time represents an absolute instant; time.Duration is an int64 of nanoseconds. Go's formatting template uses a fixed "magic time" 2006-01-02 15:04:05 — 01/02 03:04:05 PM '06 -0700 also map to 1/2/3/4/5/6/7.

package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()
    fmt.Println(now.Format("2006-01-02 15:04:05"))
    fmt.Println(now.Format(time.RFC3339))

    t, err := time.Parse(time.RFC3339, "2026-05-12T08:00:00Z")
    fmt.Println(t, err)

    loc, _ := time.LoadLocation("Asia/Shanghai")
    fmt.Println(t.In(loc))

    d := 2*time.Hour + 30*time.Minute
    future := now.Add(d)
    fmt.Println(future.Sub(now)) // 2h30m0s

    fmt.Println(now.Unix(), now.UnixMilli(), now.UnixNano())
}

Timers and Throttling

package main

import (
    "fmt"
    "time"
)

func main() {
    // delay
    time.Sleep(100 * time.Millisecond)

    // one-shot trigger
    <-time.After(100 * time.Millisecond)

    // periodic trigger
    ticker := time.NewTicker(200 * time.Millisecond)
    defer ticker.Stop()
    stop := time.After(700 * time.Millisecond)
    for {
        select {
        case t := <-ticker.C:
            fmt.Println("tick", t.Format("15:04:05.000"))
        case <-stop:
            return
        }
    }
}

encoding/json

Marshal / Unmarshal handle a whole in-memory JSON blob; NewEncoder / NewDecoder suit streaming (HTTP body, large files). Struct tags control field names, optional fields, and omission.

package main

import (
    "encoding/json"
    "fmt"
    "strings"
    "time"
)

type User struct {
    ID       int       `json:"id"`
    Name     string    `json:"name"`
    Email    string    `json:"email,omitempty"` // omit when empty
    Password string    `json:"-"`               // never serialized
    Created  time.Time `json:"created_at"`
}

func main() {
    b, _ := json.MarshalIndent(User{1, "Alice", "", "pw", time.Now()}, "", "  ")
    fmt.Println(string(b))

    var u User
    _ = json.Unmarshal(b, &u)
    fmt.Println(u.Name)

    // streaming: handle a huge JSON array
    src := `[{"id":1,"name":"a","created_at":"2026-05-12T00:00:00Z"},{"id":2,"name":"b","created_at":"2026-05-12T00:00:00Z"}]`
    dec := json.NewDecoder(strings.NewReader(src))
    dec.Token() // skip the opening [
    for dec.More() {
        var u User
        if err := dec.Decode(&u); err != nil {
            break
        }
        fmt.Println(u.ID, u.Name)
    }
}

context: Cancellation, Timeout, Values

context propagates cancellation signals, timeouts, and request-scoped data along a call chain — by convention "the first parameter of a function" across all libraries. Rule: when a parent context is cancelled, all derived child contexts are cancelled too.

package main

import (
    "context"
    "errors"
    "fmt"
    "log"
    "net/http"
    "time"
)

type traceKey struct{}

func main() {
    ctx := context.Background()                            // root
    ctx, cancel := context.WithTimeout(ctx, 2*time.Second) // timeout
    defer cancel()                                         // must defer cancel

    req, _ := http.NewRequestWithContext(ctx, "GET", "https://example.com", nil)
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        if errors.Is(err, context.DeadlineExceeded) {
            log.Println("timeout")
        } else {
            log.Println("err:", err)
        }
    } else {
        resp.Body.Close()
    }

    // pass request-scoped data (only call-chain-wide metadata like a trace id)
    ctx = context.WithValue(ctx, traceKey{}, "req-123")
    trace := ctx.Value(traceKey{}).(string)
    fmt.Println("trace:", trace)
}

errors: Wrap, Match, Unwrap

package main

import (
    "errors"
    "fmt"
)

var ErrNotFound = errors.New("not found")

type User struct{ ID int }

func Get(id int) (*User, error) {
    if id == 0 {
        return nil, fmt.Errorf("get(%d): %w", id, ErrNotFound)
    }
    return &User{ID: id}, nil
}

func main() {
    _, err := Get(0)
    if errors.Is(err, ErrNotFound) {
        fmt.Println("not found")
    }

    // aggregate multiple errors (Go 1.20+)
    joined := errors.Join(errors.New("a"), errors.New("b"))
    fmt.Println(joined)
}

sort / slices / maps

Go 1.21+ adds the generic slices and maps packages, far nicer than the old sort package.

package main

import (
    "fmt"
    "maps"
    "slices"
    "sort"
    "strings"
)

type User struct {
    Name string
    Age  int
}

func main() {
    nums := []int{3, 1, 4, 1, 5, 9}
    slices.Sort(nums)
    fmt.Println(nums)

    users := []User{{"Bob", 30}, {"Alice", 25}}
    slices.SortFunc(users, func(a, b User) int {
        return strings.Compare(a.Name, b.Name)
    })
    fmt.Println(users)

    idx, ok := slices.BinarySearch(nums, 4)
    fmt.Println(idx, ok)
    fmt.Println(slices.Contains(nums, 5), slices.Max(nums))
    fmt.Println(slices.Compact(slices.Clone(nums))) // remove adjacent duplicates

    m := map[string]int{"a": 1, "b": 2}
    keys := slices.Sorted(maps.Keys(m))
    fmt.Println(keys)
    fmt.Println(maps.Clone(m))

    // the old-style sort still works
    sort.Slice(users, func(i, j int) bool { return users[i].Age < users[j].Age })
}

regexp

package main

import (
    "fmt"
    "regexp"
)

func main() {
    re := regexp.MustCompile(`(\w+)@(\w+\.\w+)`)
    fmt.Println(re.MatchString("a@b.c"))                 // true
    fmt.Println(re.FindStringSubmatch("a@b.c"))           // [a@b.c a b.c]
    fmt.Println(re.ReplaceAllString("a@b.c", "$1 at $2")) // a at b.c

    // handle compilation failure
    if _, err := regexp.Compile(`(`); err != nil {
        fmt.Println("bad pattern:", err)
    }
}

log/slog: Structured Logging (Go 1.21+)

The new structured logging library. It supports JSON / Text handlers, attribute groups, and level control — the official option to replace logrus / zap.

package main

import (
    "errors"
    "log/slog"
    "os"
)

func main() {
    logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
        Level: slog.LevelInfo,
    }))
    slog.SetDefault(logger)

    slog.Info("user login",
        "user_id", 42,
        "ip", "127.0.0.1",
    )
    slog.Error("save failed", "err", errors.New("disk full"))
}

Other High-Frequency Packages

  • encoding/base64 · encoding/hex · encoding/csv
  • crypto/sha256 · crypto/hmac · crypto/rand
  • compress/gzip · archive/zip · archive/tar
  • net/url: URL parsing and query strings
  • os/exec: run external commands
  • os/signal: listen for SIGINT/SIGTERM for graceful shutdown
  • runtime/pprof · net/http/pprof: profiling
  • embed: embed static assets into the binary (//go:embed)