Why Go’s Generics Feel Slow: A Deep Dive into gcshape and Real‑World Pitfalls
The article reviews a year of experience with Go generics, explains the gcshape implementation, shows why generic code can be slower than ordinary code, and provides practical advice on object creation, pointer handling, method sets, and copying with concrete examples and benchmarks.
How Go Implements Generics
Go does not use the classic C++ template instantiation, TypeScript/Java type erasure, or C#'s hybrid approach; instead it introduces a concept called gcshape. Types that share the same underlying type belong to the same shape, and the compiler generates one copy of code per shape.
Performance Problems
Benchmarks comparing generic functions, generic functions that use interfaces, and ordinary code show that pure generic code is only about 10% slower, while generic‑plus‑interface code can be up to 100% slower. The slowdown comes from the extra lookup of the type dict for a type parameter and the need to convert the generic value to its concrete type, which adds a pointer indirection and a copy.
func Output[T any]() {
var t T
fmt.Printf("%#v
", t)
}
type A struct { a,b,c,d,e,f,g int64; h,i,j string; k []string; l,m,n map[string]uint64 }
type B A
func main() {
Output[string]()
Output[int]()
Output[uint]()
Output[int64]()
Output[uint64]()
Output[*string]()
Output[*int]()
Output[*uint]()
Output[*A]() // all pointers share the same shape
Output[A]()
Output[*B]()
Output[B]() // B shares code with A because their underlying types are identical
Output[[]int]()
Output[*[]int]()
Output[map[int]string]()
Output[*map[int]string]()
Output[chan map[int]string]()
}The article also shows a benchmark that measures the cost of calling generic functions versus non‑generic ones, confirming the performance penalty.
Creating Objects Inside Generic Functions
Because a type parameter T may not be directly instantiated, the only safe way to create a zero value is var ret T. Using new(T) or T{} can cause compile‑time errors for types that do not support those operations.
func F[T any]() T {
var ret T // works for any T
return ret
}If the constraint includes methods, you must still use a concrete pointer type in the signature, e.g. func Set[T int|uint](ptr *T), or introduce a second type parameter that represents a pointer to T.
Method Sets and Type Parameters
Methods defined on concrete types are not automatically available on a type parameter, even if the parameter’s underlying type matches. To call a method like Hello() on a generic value you must either constrain the parameter with an interface that contains the method or use a second pointer‑type parameter that embeds *T and the required methods.
type API[T any] interface { *T; Hello() }
func SayHello[T A|B, PT API[T]](a PT) {
a.Hello()
}Copying Objects
Shallow copies work with the usual assignment b := a. For pointers wrapped in a type parameter you need to dereference the generic pointer, copy the value, and then re‑wrap it:
func DoCopy[T any, PT API[T]](a PT) {
b := *a // shallow copy of the underlying value
(PT(&b)).Set(222222) // modify through the generic pointer
fmt.Println(a, b)
}Deep copies require an explicit Clone() method defined in an interface such as Cloneable[T any] interface { Clone() T }.
Summary and Best Practices
After a year of using Go generics the author identifies three main pain points: pointer handling, performance overhead when methods are involved, and the need to understand the core type of a constraint. Recommended practices include:
Prefer using *T explicitly instead of letting T stand for a pointer.
Use []T and map[T1]T2 directly rather than letting T represent a slice or map.
Favor generic structs over generic functions when possible.
Be aware that the core type of a constraint determines which operations are allowed.
For now, the safest approach is to rely on well‑tested generic libraries for slices and maps, and avoid more exotic generic tricks that involve pointer creation or method calls on type parameters.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
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.
