Notes — Module 15: Reflection and Metaprogramming¶
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/15. Reflection and Metaprogramming]] 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((Reflection))
Three Laws
Law 1: ValueOf/TypeOf from interface
Law 2: Interface() back to any
Law 3: settability requires pointer
reflect.Type
Name vs Kind distinction
NumField / Field(i)
StructTag.Get / Lookup
reflect.Value
Addressability
CanSet check
Set / SetInt / SetString
Call for methods
Struct Tags
json name + omitempty + dash
encoding/json uses tags
database/sql mappers
custom tag parsers
go generate
stringer tool
directive comment
generated files committed
zero reflection overhead
When to use what
reflection — truly unknown types
generics — constrained unknown types
go generate — known but numerous types
interfaces — behavioral abstraction
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".
- The pointer dance: I finally understood why you need
reflect.ValueOf(&x).Elem()instead ofreflect.ValueOf(x)— passing x copies it into the interface, and you can't set a copy. The pointer gives reflect a path back to the original memory. - Kind over Type for generic handlers: Switching on Kind (not Type) was the key insight for writing code that handles both
intandtype UserID intin the same branch. - 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.
The Three Laws of Reflection¶
Your explanation here
What I'm still unsure about: (e.g., what exactly is stored in a reflect.Value vs a reflect.Type)
reflect.Type vs reflect.Value¶
Your explanation here
What I'm still unsure about: (e.g., when do I need Type vs when do I need Value?)
Struct Tags¶
Your explanation here
What I'm still unsure about: (e.g., what happens if I call Tag.Get with an unknown key?)
Addressability and Settability¶
Your explanation here
What I'm still unsure about: (e.g., are all fields of an addressable struct addressable?)
go generate¶
Your explanation here
What I'm still unsure about: (e.g., when do I commit generated files vs regenerate each build?)
Connections to Other Topics¶
How does this module connect to things you already know?
| This module's concept | Connects to | How |
|---|---|---|
| reflect.ValueOf takes interface{} | [[go/6. Methods and Interfaces]] | The interface stores a (type, value) pair; reflection unpacks that pair |
| Struct tags and field metadata | [[go/4. Composite Types]] | Tags are part of struct field declarations; must understand structs to iterate fields |
| Reflection vs generics tradeoff | [[go/10. Generics]] | Generics solve at compile time what reflection solves at runtime; different cost/safety profiles |
| Reflection performance overhead | [[go/16. Performance and Profiling]] | Reflection calls are measurably slower; profiling shows where they matter |
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 you use reflection on channel values — send and receive? → added to QUESTIONS.md as Q001
- Is reflect.DeepEqual implemented using reflection internally? → probably yes, worth checking
- Does the compiler inline any reflect calls, or is every call truly dynamic? → added to QUESTIONS.md as Q002
Code Snippets Worth Remembering¶
Patterns, idioms, or examples that captured something important.
The settable-value pattern¶
// To set a field via reflection, always pass a pointer and call Elem()
v := reflect.ValueOf(&myStruct).Elem()
f := v.FieldByName("MyField")
if f.CanSet() {
f.SetString("new value")
}
Why I'm saving this: This is the only correct way to set values. Forgetting the & and .Elem() causes a panic that only surfaces at runtime.
The tag-parsing idiom¶
tag := field.Tag.Get("json") // "" if absent
if tag == "-" { continue } // explicitly excluded
parts := strings.SplitN(tag, ",", 2)
name := field.Name // default
if parts[0] != "" { name = parts[0] }
omitempty := len(parts) > 1 && parts[1] == "omitempty"
Why I'm saving this: This is the exact logic encoding/json uses. Internalizing it means I can reason about why a field is or isn't in JSON output.
go:generate directive¶
Why I'm saving this: The comment must start at column 0, there must be no space between // and go:generate, and the tool invocation follows the same quoting rules as a shell command. Easy to get wrong.
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.
- CanSet vs IsValid — I initially confused
CanSet()(can I modify this value?) withIsValid()(does this Value hold anything at all?). A zeroreflect.Value(returned by failedFieldByName) is not valid; calling anything on it panics. Always checkIsValid()beforeCanSet(). - Kind vs Type confusion — I initially tried to compare
v.Type() == reflect.TypeOf(0)to check for integers, which missedtype MyInt int. Switching onv.Kind()handles all int-kinded types uniformly.
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