Go Intermediate #7 Standard Library Tour
One of Go’s biggest strengths is its rich standard library. Where older JavaScript ecosystems fill gaps with third-party packages, Go ships rich standard libraries from day one. This post sweeps through the most-used parts at once.
This post is closer to an index — “this package fits this use” — than a deep dive. For detailed usage, each package’s official docs are best.
fmt — formatted I/O
#
Already covered in Basics #2, but again.
fmt.Println("hi") // automatic newline
fmt.Print("no newline")
fmt.Printf("%s = %d\n", "n", 42)
s := fmt.Sprintf("v=%v", x) // to string
fmt.Errorf("failed: %w", err) // create an error
// common verbs
// %v default
// %+v with field names
// %#v in Go syntax
// %d %s %t %f %T %qstrings — string tools
#
strings.Contains("hello", "ell") // true
strings.HasPrefix("hello", "he") // true
strings.HasSuffix("hello", "lo") // true
strings.Index("hello", "ll") // 2
strings.Replace("hello", "l", "L", -1) // heLLo (-1 means all)
strings.ReplaceAll("hello", "l", "L") // heLLo
strings.ToUpper("hello") // HELLO
strings.ToLower("HELLO") // hello
strings.TrimSpace(" hello ") // "hello"
strings.Trim("---hi---", "-") // hi
strings.Split("a,b,c", ",") // ["a", "b", "c"]
strings.Join([]string{"a", "b"}, ",") // a,b
strings.Repeat("ab", 3) // ababab
strings.Fields(" a b c ") // ["a", "b", "c"]strings.Builder — efficient string assembly
#
var b strings.Builder
b.WriteString("Hello, ")
b.WriteString("World")
result := b.String() // "Hello, World"Concatenating with s += other in a loop allocates new memory each time. Builder reuses an internal buffer for far better efficiency.
strconv — string ↔ number
#
strconv.Itoa(42) // "42"
strconv.Atoi("42") // (42, nil)
strconv.ParseInt("42", 10, 64) // (42, nil) — base, bit width
strconv.ParseFloat("3.14", 64)
strconv.FormatFloat(3.14, 'f', 2, 64) // "3.14"
strconv.Quote("hi\n") // "\"hi\\n\""In most cases, Itoa / Atoi / ParseFloat is plenty.
bytes — []byte tools
#
The byte-slice counterpart of strings, with a nearly identical API.
bytes.Contains([]byte("hello"), []byte("ll"))
bytes.Equal(a, b)
bytes.Replace(...)
// Buffer — a growable byte buffer
var buf bytes.Buffer
buf.WriteString("hi")
buf.Write([]byte("!"))
fmt.Println(buf.String())bytes.Buffer satisfies both io.Reader/io.Writer — appears often when reading or writing IO into memory.
io — IO interfaces
#
The core interfaces from #1 Interfaces.
type Reader interface { Read(p []byte) (int, error) }
type Writer interface { Write(p []byte) (int, error) }
type Closer interface { Close() error }
io.ReadAll(r) // read everything to []byte
io.Copy(dst, src) // copy from src to dst
io.EOF // end-of-read sentineldata, err := io.ReadAll(resp.Body)
defer resp.Body.Close()os — OS resources
#
os.Args // command-line arguments
os.Getenv("HOME")
os.Setenv("KEY", "value")
os.Exit(1) // terminate the program
os.Stdin / os.Stdout / os.Stderr // io.Reader/Writer
// files
file, err := os.Open("file.txt") // read
file, err := os.Create("file.txt") // write (overwrite)
defer file.Close()
data, err := os.ReadFile("file.txt") // read all at once
err := os.WriteFile("file.txt", data, 0644)
// directories
entries, err := os.ReadDir("./")
os.MkdirAll("a/b/c", 0755)
os.Remove("file.txt")
os.RemoveAll("dir")path/filepath — path handling
#
filepath.Join("a", "b", "c") // a/b/c (OS-aware)
filepath.Base("/a/b/c.go") // c.go
filepath.Dir("/a/b/c.go") // /a/b
filepath.Ext("c.go") // .go
filepath.Abs("./file") // absolute pathHandles per-OS separator differences (Linux /, Windows \) automatically.
time — time
#
now := time.Now()
yesterday := now.Add(-24 * time.Hour)
nextHour := now.Add(time.Hour)
// differences
elapsed := time.Since(start) // elapsed time
duration := time.Until(deadline) // remaining time
// comparisons
now.After(other)
now.Before(other)
now.Equal(other)Duration #
time.Second
time.Minute
time.Hour
time.Millisecond
time.Microsecond
time.Nanosecond
3 * time.Second // 3 seconds
500 * time.Millisecond // 0.5 secondsFormatting — Go’s distinctive way #
now := time.Now()
fmt.Println(now.Format("2006-01-02 15:04:05"))
// the reference time Go chose
// 2006-01-02 15:04:05.999999999 -0700 MST = Mon Jan 2 15:04:05 MST 2006
// i.e. 1 2 3 4 5 6 7Unlike other languages that use tokens like YYYY-MM-DD, Go expresses formats by writing a specific reference time (2006-01-02 15:04:05). Awkward at first, intuitive once you’re used to it.
Common constants:
time.RFC3339 // "2006-01-02T15:04:05Z07:00"
time.RFC1123
time.DateOnly // "2006-01-02"
time.TimeOnly // "15:04:05"Parse #
t, err := time.Parse(time.RFC3339, "2026-05-04T10:00:00Z")encoding/json — JSON
#
type User struct {
ID string `json:"id"`
Name string `json:"name"`
Age int `json:"age,omitempty"`
}
// serialize
data, err := json.Marshal(user)
data, err := json.MarshalIndent(user, "", " ") // pretty
// deserialize
var u User
err := json.Unmarshal(data, &u)
// streaming
encoder := json.NewEncoder(os.Stdout)
encoder.Encode(user)
decoder := json.NewDecoder(file)
err := decoder.Decode(&u)json: tags — the part we saw in #6 Structs. omitempty says to omit the field from output when it’s the zero value.
Dynamic JSON — map[string]any
#
When dealing with JSON of an unknown schema.
var data map[string]any
json.Unmarshal(rawJSON, &data)
if name, ok := data["name"].(string); ok {
fmt.Println(name)
}Requiring a type assertion on every access gets cumbersome. Defining a struct is cleaner when the schema is known.
sort — sorting
#
sort.Ints(nums)
sort.Strings(names)
sort.Float64s(values)
// sort a slice — by function
sort.Slice(users, func(i, j int) bool {
return users[i].Age < users[j].Age
})
// search (in an already-sorted slice)
i := sort.SearchInts(nums, target)After generics — slices
#
The slices package was added in Go 1.21+. A more modern API.
import "slices"
slices.Sort(nums)
slices.SortFunc(users, func(a, b User) int {
return a.Age - b.Age
})
slices.Contains([]string{"a", "b"}, "a")
slices.Index([]int{10, 20, 30}, 20) // 1
slices.Reverse(nums)
slices.Min(nums)
slices.Max(nums)In new code the slices package is becoming increasingly common.
maps — map tools
#
import "maps"
maps.Keys(m) // all keys (iterable; collect to a slice with slices.Collect)
maps.Values(m)
maps.Equal(a, b)
maps.Clone(m)
maps.Delete(m, fn)errors — error tools
#
Detailed in #2 Error Handling.
errors.New("message")
errors.Is(err, target)
errors.As(err, &target)
errors.Join(err1, err2, err3)
errors.Unwrap(err)net/http — HTTP client and server
#
resp, err := http.Get("https://example.com")
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
// custom client
client := &http.Client{Timeout: 5 * time.Second}
req, _ := http.NewRequestWithContext(ctx, "POST", url, body)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)http.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "users")
})
http.ListenAndServe(":8080", nil)Covered in earnest in the Practice series.
regexp — regular expressions
#
re := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})`)
match := re.FindStringSubmatch("2026-05-04")
// match: ["2026-05-04", "2026", "05", "04"]
re.MatchString("foo")
re.ReplaceAllString("a1 b2", "X")
re.FindAllString("a1 b2 c3", -1)bufio — buffered IO
#
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
// ...
}
if err := scanner.Err(); err != nil {
// ...
}
// or Reader
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')When reading a large file line by line — a memory-safe pattern compared to os.ReadFile.
log and log/slog — logging
#
The old log does plain output.
log.Println("info")
log.Fatalf("fatal: %v", err) // prints, then os.Exit(1)Go 1.21+’s log/slog — structured logging (key-value pairs).
import "log/slog"
slog.Info("user logged in", "user_id", "u1", "ip", "127.0.0.1")
slog.Error("DB failed", "err", err)
// JSON output
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
slog.SetDefault(logger)JSON output makes integration with log analytics tools (Datadog, Grafana, etc.) very easy. In new code, slog is increasingly the standard choice.
flag — command-line flags
#
import "flag"
var (
host = flag.String("host", "localhost", "server address")
port = flag.Int("port", 8080, "port")
verbose = flag.Bool("v", false, "verbose")
)
func main() {
flag.Parse()
fmt.Println(*host, *port, *verbose)
}./app --host=0.0.0.0 --port=3000 -vFor richer CLI features, reach for external libraries like cobra or kong. The standard flag is enough for small tools.
Other commonly used packages — briefly #
crypto/rand secure random
crypto/sha256 hash
encoding/base64
encoding/hex
encoding/csv
encoding/xml
hash/fnv fast hash (not secure)
math
math/rand non-secure random
net low-level networking
net/url URL parsing
sync Mutex, WaitGroup, Once, Map
sync/atomic atomic operations
unicode unicode tools
unicode/utf8 UTF-8 encodingWrap-up #
This post was closer to an index. For detailed usage see each package on pkg.go.dev/std.
Common cases:
- Strings →
strings,fmt,strconv - Time →
time - JSON →
encoding/json - Sorting →
sortorslices(Go 1.21+) - HTTP →
net/http - Logging →
log/slog(Go 1.21+) - IO →
os,io,bufio
Closing the Intermediate series #
What we covered across 7 posts:
- Interfaces — implicit implementation (#1)
- Error handling — wrap, Is/As, panic (#2)
- Goroutines and channels — concurrency intro (#3)
- select and timeouts — handling many channels (#4)
- context — standard cancellation/timeout tool (#5)
- Testing — testing package and table-driven (#6)
- Standard library tour — packages you’ll meet often (this post)
With this foundation you can write robust Go code. The next Advanced series goes one step further — concurrency patterns in depth, the memory model, generics, and reflect-level tools.