Notes — Module 10: Generics¶
These are your personal study notes. Write freely and honestly. Incomplete notes are fine — they show where your understanding still needs work. Return to this file to add insights as they develop over time.
Module: [[go/10. Generics]] Topic: [[go]] Date started: YYYY-MM-DD Status: In progress
Concept Map¶
Sketch how the concepts in this module relate to each other. Fill in the Mermaid diagram.
mindmap
root((Generics))
Type Parameters
syntax: func F[T C](...)
type parameter list in []
multiple parameters: [K, V]
Constraints
interfaces as constraints
any — no operations
comparable — == and !=
cmp.Ordered — < > <= >=
union elements |
tilde ~ underlying type
Generic Types
struct with type parameters
Stack[T any]
Set[T comparable]
methods reference T, not new params
Type Inference
compiler solves T from arguments
explicit when T only in return type
Standard Packages
slices — Sort SortFunc Contains
maps — Keys Values Clone
cmp — Compare Ordered
When NOT to Use
method dispatch → use interface
one concrete type → skip it
no duplication → YAGNI
Alternative: draw this on paper, photo it, and link the image here.
Key Insights¶
The "aha moments" — the things that, once understood, made the rest clear. Be specific: "I finally understood X because Y" is more useful than "X makes sense".
- Constraints are interfaces: I finally understood that generics don't add a new concept — constraints reuse the existing interface mechanism. An interface that describes a type set is the same thing as an interface that describes a method set, just with type elements instead of method elements.
~solves the named-type problem: The tilde operator was the insight I needed: without it,type Celsius float64doesn't satisfyfloat64in a constraint. With~float64, any type whose underlying type is float64 — including Celsius — satisfies the constraint.- Add insights as you discover them
My Understanding¶
Explain the core concepts in your own words, as if teaching them to someone else. If you can't explain it simply, you don't understand it well enough yet.
Type Parameters¶
Your explanation here
What I'm still unsure about: (e.g., when does type inference fail and I need to be explicit?)
Constraints¶
Your explanation here
What I'm still unsure about: (e.g., can I mix method elements and type elements in the same constraint? What does that mean?)
The ~ Token¶
Your explanation here
What I'm still unsure about: (e.g., what is "underlying type" for a struct vs a named scalar?)
Generic Types (Stack, Set)¶
Your explanation here
What I'm still unsure about: (e.g., why can't methods add new type parameters?)
When to Use Generics vs Interfaces¶
Your explanation here
Connections to Other Topics¶
How does this module connect to things you already know?
| This module's concept | Connects to | How |
|---|---|---|
| Constraints as interfaces | [[go/6. Methods and Interfaces]] | Constraints extend the interface mechanism; an interface used as a constraint can contain type elements (unions) in addition to method elements |
| Generic Stack[T] wrapping []T | [[go/4. Composite Types]] | The Stack stores items in a []T slice internally; all slice operations (append, len, slicing) work on []T just as they do on []int |
| Testing generic functions | [[go/11. Testing and Benchmarking]] | Table-driven tests should include rows for each interesting type instantiation; benchmark generic vs concrete to understand the overhead |
Questions That Arose¶
Log questions as they appear. Don't stop to answer them now — just capture them. Then move the serious ones to QUESTIONS.md.
- Can a constraint contain both method elements and type elements? What does that mean at the call site? → added to QUESTIONS.md as Q001
- What happens with generics and reflect — does reflect.TypeOf return the concrete type T or the type parameter? → might be answered in a later module
- Is there a way to get the name/kind of T at runtime without reflect? → likely no; add to QUESTIONS.md
Code Snippets Worth Remembering¶
Patterns, idioms, or examples that captured something important.
The zero-value pattern for generic returns¶
func First[T any](s []T) (T, bool) {
if len(s) == 0 {
var zero T // zero value of T, whatever T is
return zero, false
}
return s[0], true
}
Why I'm saving this: var zero T is the idiomatic way to get a zero value for an unknown type. You cannot write return nil, false because T may not be a pointer or interface type.
The constraint with ~ pattern¶
type Ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64 | ~string
}
Why I'm saving this: This is the shape of cmp.Ordered. The ~ is what makes it inclusive of user-defined named types. Understanding this one interface unlocks most numeric/ordered generic functions.
The method-cannot-have-type-params workaround¶
// What you want (doesn't compile):
// func (s *Stack[T]) Map[U any](f func(T) U) []U { ... }
// What you write instead (package-level function):
func StackMap[T, U any](s *Stack[T], f func(T) U) []U {
result := make([]U, s.Len())
for i, v := range s.items {
result[i] = f(v)
}
return result
}
Why I'm saving this: This restriction surprises everyone. The workaround — flip to a package-level function where the receiver becomes a regular parameter — is idiomatic and straightforward.
slices.SortFunc with cmp.Compare¶
slices.SortFunc(people, func(a, b Person) int {
if c := cmp.Compare(a.Age, b.Age); c != 0 {
return c
}
return cmp.Compare(a.Name, b.Name)
})
Why I'm saving this: This is the modern idiomatic sort pattern. cmp.Compare returns -1/0/+1, the chained-if pattern handles multi-key sorts cleanly, and slices.SortFunc is type-safe unlike the old sort.Slice.
What Tripped Me Up¶
Mistakes I made, misconceptions I had, things that confused me more than they should have. Being honest here helps you later.
anyvscomparable— I initially tried to use==inside a function constrained byany, and got a confusing compile error. It clicked when I realizedanymeans "you can't do anything type-specific with T" — it only permits assignment and passing.- Forgetting
~— I wrote a constraintfloat32 | float64and was confused whentype Celsius float64didn't satisfy it. The fix is always~float32 | ~float64.
Summary in My Own Words¶
Write a 3–5 sentence summary of this entire module without looking at any notes. If you can't do this, you need more study time.
Write your summary here after completing the module.
Last updated: YYYY-MM-DD