Unlock Go 1.18 Generics: Practical Tips, Generic Sorting, Heap, and Pool Enhancements
This article explains Go 1.18 generics, covering type parameters, type sets, type inference, and shows how to simplify sorting, strconv.Append, heap, sync.Pool, and sync.Map with concise generic implementations and real code examples.
Introduction
Go 1.18 introduced generics, the biggest language change, and this article collects many generic usage techniques with concrete code examples, showing how to modify built‑in libraries and compare before‑after implementations.
Generic Basics
Type Parameters
Functions and types can now declare type parameters.
Type Sets
Interfaces can be used as type sets to simplify constraints.
Type Inference
The compiler can infer type arguments in most calls.
<code>func FuncName[P, Q constraint1, R constraint2, ...](parameter1 P, parameter2 Q, ...) (R, Q, ...)</code>Generic Sorting Function
Standard sort.Sort requires a type to implement sort.Interface. Using generics we can write a universal sorter.
<code>// generic sortable implementation
type sortable[E any] struct {
data []E
cmp base.CMP[E]
}
func (s sortable[E]) Len() int { return len(s.data) }
func (s sortable[E]) Swap(i, j int) { s.data[i], s.data[j] = s.data[j], s.data[i] }
func (s sortable[E]) Less(i, j int) bool { return s.cmp(s.data[i], s.data[j]) >= 0 }
func Sort[E any](data []E, cmp base.CMP[E]) {
sortobject := sortable[E]{data: data, cmp: cmp}
sort.Sort(sortobject)
}</code>Usage example with a Person slice shows the code reduction.
Generic Append Helper
The strconv Append* family requires different functions for each type. A generic Append can handle any value.
<code>// Append convert e to string and appends to dst
func Append[E any](dst []byte, e E) []byte {
toAppend := fmt.Sprintf("%v", e)
return append(dst, []byte(toAppend)...)
}</code>Generic Heap Container
By wrapping container/heap with a generic struct we avoid writing separate heap types.
<code>type heapST[E any] struct {
data []E
cmp base.CMP[E]
}
func (h *heapST[E]) Len() int { return len(h.data) }
func (h *heapST[E]) Less(i, j int) bool { return h.cmp(h.data[i], h.data[j]) < 0 }
func (h *heapST[E]) Swap(i, j int) { h.data[i], h.data[j] = h.data[j], h.data[i] }
func (h *heapST[E]) Push(x any) { h.data = append(h.data, x.(E)) }
func (h *heapST[E]) Pop() any {
old := h.data
n := len(old)
x := old[n-1]
h.data = old[0 : n-1]
return x
}
type Heap[E any] struct { data *heapST[E] }
func NewHeap[E any](t []E, cmp base.CMP[E]) *Heap[E] {
ret := heapST[E]{data: t, cmp: cmp}
heap.Init(&ret)
return &Heap[E]{&ret}
}
func (h *Heap[E]) Push(v E) { heap.Push(h.data, v) }
func (h *Heap[E]) Pop() E { return heap.Pop(h.data).(E) }
</code>Generic sync.Pool Wrapper
A generic Pool removes the need for type assertions.
<code>type Pool[E any] struct {
New base.Supplier[E]
internal sync.Pool
}
func NewPool[E any](f base.Supplier[E]) *Pool[E] {
p := Pool[E]{New: f}
p.internal = sync.Pool{New: func() any { return p.New() }}
return &p
}
func (p *Pool[E]) Get() E { return p.internal.Get().(E) }
func (p *Pool[E]) Put(v E) { p.internal.Put(v) }
</code>Generic sync.Map Wrapper
Provides type‑safe operations, existence checks, min/max key/value, and conversion helpers.
<code>type Map[K comparable, V any] struct {
mp sync.Map
empty V
mu sync.Mutex
}
func NewMap[K comparable, V any]() *Map[K, V] { return &Map[K, V]{mp: sync.Map{}} }
// ... methods Exist, ExistValue, MinKey, MaxValue, etc.
</code>Conclusion
Generics dramatically reduce boilerplate, improve readability and safety. The article showcases several practical patterns and points to the open‑source goassist project for more examples.
Architecture & Thinking
🍭 Frontline tech director and chief architect at top-tier companies 🥝 Years of deep experience in internet, e‑commerce, social, and finance sectors 🌾 Committed to publishing high‑quality articles covering core technologies of leading internet firms, application architecture, and AI breakthroughs.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.