Stdlib Generic Packages — Junior Level¶
Table of Contents¶
- Introduction
- Prerequisites
- Glossary
- Core Concepts
- Real-World Analogies
- Mental Models
- Pros & Cons
- Use Cases
- Code Examples
- Coding Patterns
- Clean Code
- Product Use / Feature
- Error Handling
- Security Considerations
- Performance Tips
- Best Practices
- Edge Cases & Pitfalls
- Common Mistakes
- Common Misconceptions
- Tricky Points
- Test
- Tricky Questions
- Cheat Sheet
- Self-Assessment Checklist
- Summary
- What You Can Build
- Further Reading
- Related Topics
- Diagrams & Visual Aids
Introduction¶
Focus: "What does the stdlib give us, and how do we use it?"
When generics shipped in Go 1.18, the standard library deliberately stayed almost empty of them. The team wanted to watch how the community used type parameters before promoting helpers to stdlib. One year later, in Go 1.21 (August 2023), three packages landed and immediately replaced dozens of hand-written helpers in real codebases:
slices— algorithms that operate on slices: search, sort, equal, min/max, insert, delete.maps— utilities for maps: keys, values, clone, equal, copy, deletion by predicate.cmp— comparison helpers and thecmp.Orderedconstraint shared by every "this T can be compared with<" function in the language.
These three packages are the first place a Go programmer should look before writing func ContainsX(...), func MapKeys(...), or func MinFloat64(...). The chance is high that the function already exists, is faster than what you would write, and is supported on every platform Go runs on.
import (
"cmp"
"fmt"
"slices"
)
func main() {
nums := []int{3, 1, 4, 1, 5, 9, 2, 6}
slices.Sort(nums)
fmt.Println(nums) // [1 1 2 3 4 5 6 9]
fmt.Println(slices.Contains(nums, 5)) // true
fmt.Println(slices.Index(nums, 9)) // 7
fmt.Println(slices.Max(nums)) // 9
fmt.Println(cmp.Compare(3, 5)) // -1
}
After reading this file you will: - Know which package to reach for when you need a slice, map, or comparison helper - Recognize the most common functions by signature - Tell the difference between slices.Index and slices.IndexFunc at a glance - Understand the role of cmp.Ordered as the universal "ordered" constraint
Prerequisites¶
- Go 1.21 or newer in
go.mod - Basic familiarity with type parameters (
[T any],[T comparable]) - Knowledge of slices, maps, and
for range - Familiarity with
func(a, b T) intcomparator style
Glossary¶
| Term | Definition |
|---|---|
slices | Stdlib package of generic slice algorithms (Go 1.21+) |
maps | Stdlib package of generic map utilities (Go 1.21+) |
cmp | Stdlib package of comparison helpers (Go 1.21+) |
cmp.Ordered | Constraint for types usable with <, <=, >, >= |
cmp.Compare | Returns -1, 0, +1 for a<b, a==b, a>b |
cmp.Less | Boolean version of Compare |
cmp.Or | Returns first non-zero argument (Go 1.22+) |
| Comparator | A func(a, b T) int returning -1, 0, +1 |
| In-place API | Mutates the input slice (e.g., slices.Sort) |
| Copying API | Returns a new slice (e.g., slices.Clone) |
Core Concepts¶
1. The slices package — algorithms over a slice¶
slices collects the algorithms every Go team used to write by hand. Highlights:
slices.Contains(s, target) // bool
slices.Index(s, target) // int — -1 if absent
slices.Equal(a, b) // bool
slices.Sort(s) // in-place
slices.IsSorted(s) // bool
slices.Reverse(s) // in-place
slices.Concat(a, b, c) // []T (1.22+)
slices.Min(s) // T (panics if empty)
slices.Max(s) // T (panics if empty)
slices.BinarySearch(s, target) // (idx int, found bool)
slices.Insert(s, i, v...) // []T
slices.Delete(s, i, j) // []T
slices.Clone(s) // []T
slices.Compact(s) // []T (deduplicate adjacent)
The constraint depends on the function. Contains and Index need comparable. Sort and Min/Max need cmp.Ordered. Equal needs comparable. Reverse and Clone need only any.
2. The maps package — map utilities¶
maps is smaller than slices because maps are simpler:
maps.Keys(m) // iter.Seq[K] in 1.23+, was []K in early drafts
maps.Values(m) // iter.Seq[V] in 1.23+
maps.Equal(a, b) // bool
maps.Clone(m) // map[K]V
maps.Copy(dst, src) // copies src into dst
maps.DeleteFunc(m, fn) // removes entries where fn(k, v) is true
In Go 1.23+, maps.Keys and maps.Values return an iterator (iter.Seq[K]). To build a slice you wrap with slices.Collect:
3. The cmp package — comparison helpers¶
cmp is tiny but central:
type Ordered interface { /* int, float, string family */ }
func Compare[T Ordered](a, b T) int // -1, 0, +1
func Less[T Ordered](a, b T) bool // a < b
func Or[T comparable](vals ...T) T // first non-zero (1.22+)
cmp.Compare is the standard comparator every *Func API expects. cmp.Or replaces ten years of "first non-empty string" helper functions.
4. Naming convention: plain vs Func variant¶
Most slices algorithms come in two flavours:
Plain (uses == or <) | Func (uses callback) |
|---|---|
slices.Contains | slices.ContainsFunc |
slices.Index | slices.IndexFunc |
slices.Sort | slices.SortFunc |
slices.Compact | slices.CompactFunc |
slices.BinarySearch | slices.BinarySearchFunc |
The plain version requires comparable or cmp.Ordered. The Func version takes a callback so any T works — even slices, maps, structs with non-comparable fields.
5. The constraint shape table¶
| Function family | Constraint | Why |
|---|---|---|
Contains, Index, Equal | comparable | uses == |
Sort, Min, Max, BinarySearch | cmp.Ordered | uses < |
SortFunc, IndexFunc | any | callback handles compare |
Reverse, Clone, Concat | any | no comparison |
Real-World Analogies¶
Analogy 1 — A toolbox
slices is your hammer-and-screwdriver toolbox. Every operation you need on a slice is already in there. Reaching for hand-rolled code is like forging your own hammer.
Analogy 2 — A measuring stick (cmp)
cmp.Compare is the universal measuring stick: every sort, search, and "smaller of two" call agrees on the same -1/0/+1 convention.
Analogy 3 — Maps as bags of stuff
maps.Keys, maps.Values, maps.Equal, maps.Clone are like a checklist of "things you can do to a bag without caring what is inside it".
Mental Models¶
Model 1 — "Look in slices first"¶
Before writing any helper that operates on a []T, search godoc for slices.X. Most likely it exists.
Model 2 — "Plain or Func?"¶
Ask: "Do my elements support == or <?" Yes → plain. No or "I need custom comparison" → Func variant.
Model 3 — "cmp.Compare is the glue"¶
slices.SortFunc(people, func(a, b Person) int { return cmp.Compare(a.Age, b.Age) }) — this idiom is everywhere in modern Go.
Pros & Cons¶
Pros¶
| Benefit | Why it matters |
|---|---|
| Battle-tested algorithms | Heavily benchmarked by the Go team |
Removes sort.Slice boilerplate | Less code per call site |
| Type-safe at compile time | No more interface{} plus assertions |
| Single import for most needs | slices, maps, cmp cover 90% |
| Stable API | Promised compatibility from 1.21 onward |
Cons¶
| Drawback | Why it matters |
|---|---|
| Requires Go 1.21+ | Older codebases must upgrade |
| Some functions panic on empty input | slices.Min panics; you must guard |
maps.Keys switched to iterators | Behaviour change between 1.21 and 1.23 |
| Not every helper is here | Reduce, GroupBy, Chunk are absent |
Use Cases¶
- Sorting people by age with
slices.SortFuncandcmp.Compare - Finding the maximum of a config slice with
slices.Max - Deduplicating a sorted slice with
slices.Compact - Cloning a slice or map before mutating
- Computing the union of keys across two maps
Code Examples¶
Example 1 — Sort and search¶
package main
import (
"fmt"
"slices"
)
func main() {
s := []string{"banana", "apple", "cherry"}
slices.Sort(s)
fmt.Println(s) // [apple banana cherry]
idx, ok := slices.BinarySearch(s, "banana")
fmt.Println(idx, ok) // 1 true
}
Example 2 — Sort structs with cmp.Compare¶
import (
"cmp"
"slices"
)
type Person struct {
Name string
Age int
}
func main() {
p := []Person{{"Bob", 30}, {"Ann", 25}, {"Cy", 27}}
slices.SortFunc(p, func(a, b Person) int {
return cmp.Compare(a.Age, b.Age)
})
// p is now sorted by Age ascending
}
Example 3 — Deduplicate a sorted slice¶
Compact removes adjacent duplicates, so the input must be sorted first.
Example 4 — Map clone¶
import "maps"
orig := map[string]int{"a": 1, "b": 2}
copy := maps.Clone(orig)
copy["c"] = 3
fmt.Println(orig) // {"a":1, "b":2}
fmt.Println(copy) // {"a":1, "b":2, "c":3}
Example 5 — Delete entries by predicate¶
m := map[string]int{"a": 1, "b": 2, "c": 3}
maps.DeleteFunc(m, func(k string, v int) bool {
return v%2 == 0
})
fmt.Println(m) // {"a":1, "c":3}
Example 6 — cmp.Or for first non-empty¶
cmp.Or returns the first non-zero argument. With Go 1.22+, this idiom replaces every "if a != \"\" return a; if b != \"\" return b" chain.
Coding Patterns¶
Pattern 1 — Sort by multiple keys with cmp.Or¶
slices.SortFunc(people, func(a, b Person) int {
return cmp.Or(
cmp.Compare(a.Last, b.Last),
cmp.Compare(a.First, b.First),
cmp.Compare(a.Age, b.Age),
)
})
Pattern 2 — Pre-sort, then BinarySearch¶
O(log n) lookup once you have paid for sorting.
Pattern 3 — Build a slice from map keys¶
In 1.21/1.22 the API returned []K directly; from 1.23 onward it returns an iterator and you must collect.
Pattern 4 — Equal check with custom comparator¶
Clean Code¶
- Prefer
slices.Xandmaps.Xto hand-rolled equivalents. - Prefer
cmp.Compareoverif a < b ... else ...insideSortFunc. - Sort before deduplicating with
slices.Compact. - Do not write
MyKeys[K, V]ifmaps.Keysalready exists.
Product Use / Feature¶
Real product scenarios:
- Leaderboard sorting —
slices.SortFuncwithcmp.Compareon score - Search-by-prefix in a sorted dictionary using
slices.BinarySearchFunc - Diffing config maps with
maps.Equal - Cloning request headers with
maps.Clone - Returning the highest version with
slices.Max
Error Handling¶
The stdlib generic packages do not return errors — they panic on truly invalid input:
slices.Min([]T{})andslices.Max([]T{})panic on empty slicesslices.Insertpanics on out-of-range indexslices.Deletepanics oni > jor out-of-range
Always guard the empty case for Min/Max:
Security Considerations¶
slices.Cloneproduces a shallow copy. Pointer-typed elements still share the underlying data.maps.Cloneis also shallow.- Comparing untrusted strings with
cmp.Compareis fine for ordering but not constant-time — usecrypto/subtle.ConstantTimeComparefor secret comparison.
Performance Tips¶
slices.ContainsisO(n). For sorted data,slices.BinarySearchisO(log n).slices.Sortuses an in-place sort. For copy-and-sort, useslices.Sorted(1.23+) orslices.Clonefirst.slices.Compactreturns a slice that aliases the input — the trailing region is unspecified.maps.Cloneis faster than a for-range copy because it knows the map's internal layout.
Best Practices¶
- Default to
slices/maps/cmpbefore writing your own. - Pin Go 1.21+ in
go.modso the imports are available. - Sort with
slices.SortFunc(s, cmp.Compare)for the ordered-but-Func case. - Use
cmp.Orto replace nested "first-non-empty" chains. - Read the godoc page at https://pkg.go.dev/slices before writing helpers.
- Guard
slices.Min/Maxagainst empty input.
Edge Cases & Pitfalls¶
1. slices.Min/Max panic on empty input¶
2. slices.Compact requires sorted input¶
Sort first.
3. maps.Keys is unordered¶
Map iteration order is randomized. Sort the keys if you need determinism:
4. slices.Delete zeroes the trailing elements¶
To allow garbage collection, slices.Delete writes the zero value into the freed slots. The returned slice has the right length, but the underlying array's tail is no longer your data.
5. slices.Insert may reallocate¶
If the input has insufficient capacity, Insert returns a new slice. Always reassign: s = slices.Insert(s, i, v...).
Common Mistakes¶
- Writing your own
Containsfor[]int. Useslices.Contains. - Sorting with
sort.Slicein new code. Useslices.SortFunc. - Calling
slices.Minon empty input without a guard. - Forgetting to sort before
slices.Compact. - Assuming
maps.Keysreturns a sorted slice. It does not. - Not reassigning the result of
slices.Insert/Delete.
Common Misconceptions¶
- "
slices.Sortis a stable sort." No — it ispdqsort, not stable. Useslices.SortStableFuncfor stable. - "
maps.Equalchecks deep equality." No — it uses==on values. For deep equality usemaps.EqualFuncwithreflect.DeepEqual. - "
cmp.Compareworks on any type." No — only oncmp.Ordered. - "
maps.Keysreturns a slice." From 1.23 it returns an iterator (iter.Seq[K]).
Tricky Points¶
slices.Sorton[]float64ordersNaNfirst (percmp.Comparerule).slices.Equalandmaps.EqualtreatNaN != NaN— two slices ofNaNare not equal.slices.Compactmay return a slice with reduced length but the same capacity.cmp.Orreturns the first non-zero argument; the meaning of "zero" is the type's zero value.
Test¶
- Which Go version added
slices,maps,cmpto the stdlib? - Name three functions in
slicesthat requirecmp.Ordered. - What is the return type of
cmp.Compare? - Does
slices.Sortproduce a stable sort? - What does
cmp.Or("","", "x", "y")return? - What constraint does
slices.Containsrequire? - What is the difference between
slices.Indexandslices.IndexFunc? - What does
maps.Keysreturn in Go 1.23?
(Answers: 1) 1.21; 2) Sort, Min, Max, BinarySearch, IsSorted; 3) int; 4) no; 5) "x"; 6) comparable; 7) Index uses ==, IndexFunc takes a predicate; 8) iter.Seq[K].)
Tricky Questions¶
Q1. What is the result of slices.Compact([]int{1, 2, 1, 2})? A. [1 2 1 2] — Compact only removes adjacent duplicates.
Q2. Does slices.Sort allocate? A. No — it is fully in-place.
Q3. What does cmp.Or() (no arguments) return? A. The zero value of T — but you cannot infer T with no arguments, so this fails to compile.
Cheat Sheet¶
// slices
slices.Contains(s, x) // ==
slices.ContainsFunc(s, pred)
slices.Index(s, x)
slices.Equal(a, b) // ==
slices.EqualFunc(a, b, eq)
slices.Sort(s) // < (cmp.Ordered)
slices.SortFunc(s, cmp) // -1/0/+1
slices.SortStableFunc(s, cmp)
slices.IsSorted(s)
slices.Reverse(s)
slices.Concat(a, b, c)
slices.Min(s); slices.Max(s)
slices.BinarySearch(s, x)
slices.BinarySearchFunc(s, x, cmp)
slices.Insert(s, i, v...)
slices.Delete(s, i, j)
slices.Clone(s)
slices.Compact(s); slices.CompactFunc(s, eq)
// maps
maps.Keys(m); maps.Values(m)
maps.Equal(a, b); maps.EqualFunc(a, b, eq)
maps.Clone(m); maps.Copy(dst, src)
maps.DeleteFunc(m, pred)
// cmp
cmp.Ordered // constraint
cmp.Compare(a, b) // -1/0/+1
cmp.Less(a, b) // bool
cmp.Or(v1, v2, v3) // first non-zero
Self-Assessment Checklist¶
- I know which Go release added these packages.
- I can find any common slice algorithm in the godoc for
slices. - I can sort a struct slice with
slices.SortFuncpluscmp.Compare. - I know the difference between
SortandSortStableFunc. - I know that
maps.Keysis unordered. - I have replaced at least one hand-written helper with a stdlib call.
Summary¶
The slices, maps, and cmp packages are the foundation of generic-flavoured Go since 1.21. They cover the majority of "I just need a helper for slices/maps" cases. Each package has a tiny surface, but together they removed thousands of duplicated helpers from real codebases.
The two big idioms to internalize:
slices.SortFunc(s, cmp.Compare)for ordered-but-callback-style sortingcmp.Or(a, b, c)for "first non-zero" chains
Once slices, maps, and cmp are part of your muscle memory, you stop writing func contains(...) forever.
Move on to middle.md to learn the *Func variants and multi-key sort patterns.
What You Can Build¶
After this section you can build:
- A leaderboard with
slices.SortFuncpluscmp.Compare - A deduplication pipeline with
slices.Sortplusslices.Compact - A diff utility with
maps.Equal - A "first non-empty" config resolver with
cmp.Or - A set wrapper backed by a map with
maps.Keysfor iteration
Further Reading¶
slicespackagemapspackagecmppackage- Go 1.21 release notes
- Go 1.22 release notes
- Go 1.23 release notes — iterators
Related Topics¶
- 4.13
comparableandcmp.Ordered— the constraint behind these APIs - 4.7 Generic Performance — when stdlib generics inline cleanly
- 3.x Sort package — the pre-1.21 way that still works
- iter.Seq — the new iterator type used by
maps.Keyssince 1.23
Diagrams & Visual Aids¶
The three packages at a glance¶
┌─────────┐ ┌──────┐ ┌───────┐
│ slices │ │ maps │ │ cmp │
├─────────┤ ├──────┤ ├───────┤
│ Sort │ │ Keys │ │ Ord. │
│ Search │ │ Vals │ │ Comp. │
│ Equal │ │ Equal│ │ Less │
│ Compact │ │ Clone│ │ Or │
│ Insert │ │ Copy │ │ │
│ Delete │ │ DelF │ │ │
└─────────┘ └──────┘ └───────┘
\\ | /
\\ | /
\\ | /
Common idiom: SortFunc(s, cmp.Compare)
Plain vs Func variant¶
slices.Contains ←→ slices.ContainsFunc
slices.Index ←→ slices.IndexFunc
slices.Sort ←→ slices.SortFunc
slices.Compact ←→ slices.CompactFunc
slices.BinarySearch←→ slices.BinarySearchFunc
Plain: needs comparable / cmp.Ordered
Func : works for any T, callback decides
Decision tree — which function do I want?¶
Need to find a value in a slice?
├── elements are comparable → slices.Contains / Index
├── need a predicate → slices.ContainsFunc / IndexFunc
└── slice is sorted → slices.BinarySearch / BinarySearchFunc
Need to sort?
├── elements are cmp.Ordered → slices.Sort
├── need custom comparator → slices.SortFunc
└── need stability → slices.SortStableFunc
Need to deduplicate?
├── adjacent only → slices.Compact / CompactFunc
└── full dedup → Sort first, then Compact
Need to operate on a map?
├── enumerate keys → maps.Keys (+ slices.Collect)
├── enumerate values → maps.Values (+ slices.Collect)
├── shallow copy → maps.Clone
├── compare two → maps.Equal / EqualFunc
└── filter entries → maps.DeleteFunc
Need to compare two values?
├── total ordering -1/0/+1 → cmp.Compare
├── boolean less → cmp.Less
└── first non-zero → cmp.Or
Constraint table¶
+--------------------+--------------------------+-------------------+
| Constraint | Allowed operations | Typical use |
+--------------------+--------------------------+-------------------+
| any | none specific | *Func variants |
| comparable | == != map keys | Contains, Index |
| cmp.Ordered | < <= > >= == | Sort, Min, Max |
+--------------------+--------------------------+-------------------+
Tip: prefer named imports¶
These three import paths are short on purpose. A typical file that uses generics imports all three at the top and treats them as part of the toolbox. There is no slicesutil or mapsutil to keep around.