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 charspackage 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)