Day 016 — Interfaces & Implicit Satisfaction¶
Month 1 · Week 3 · ⬅ Day 015 · Day 017 ➡ · Journal index
🎯 Learning Objective¶
Define and consume interfaces in Go, understand implicit (structural) satisfaction, and avoid the nil-interface trap.
📚 Topics¶
- Interfaces as method sets · implicit satisfaction (no
implementskeyword) - "Accept interfaces, return structs" · small interfaces
- The two-word interface value: (type, value) · nil interface vs nil concrete
- Empty interface
interface{}/any· embedding interfaces
📖 Reading / Sources¶
- Learning Go (Bodner) ch.7 — Interfaces
- Tour of Go — Interfaces
- Effective Go — Interfaces
- Go blog — Errors are values (interface design mindset)
📝 Notes¶
- An interface is a set of method signatures. A type satisfies it simply by having those methods — no declaration of intent is needed. This is structural/implicit satisfaction → [[interfaces]].
- This decoupling means you can write an interface in package
Athat types in packageBsatisfy withoutBever importingA. Define interfaces where they are consumed, not where the concrete type lives. - Idiom: "Accept interfaces, return concrete types." Keep interfaces small —
io.Reader/io.Writerare one method each and compose everywhere. - Whether a type implements an interface depends on method sets → [[value-vs-pointer-receivers]]: if
Speakhas a pointer receiver, only*DogimplementsSpeaker, notDog. - An interface value is a two-word pair: (concrete type, value). It is
nilonly when both are nil. The classic nil-interface trap: returning a*MyErrortyped nil pointer as anerrormakes the interface non-nil (type is set), soif err != nilis unexpectedly true → [[nil-interface-trap]]. any(alias forinterface{}, Go 1.18+) is the empty interface — every type satisfies it. Prefer concrete or small interfaces; reach foranyonly when you truly accept anything.- Interfaces can embed other interfaces:
io.ReadWriterembedsReaderandWriter. A compile-time satisfaction check is idiomatic:var _ Speaker = (*Dog)(nil).
💻 Code Examples¶
type Speaker interface{ Speak() string }
type Dog struct{ Name string }
func (d Dog) Speak() string { return d.Name + " says woof" }
// Compile-time assertion that Dog satisfies Speaker (fails to compile otherwise).
var _ Speaker = Dog{}
func announce(s Speaker) { fmt.Println(s.Speak()) } // accept the interface
func main() {
announce(Dog{Name: "Rex"}) // Dog never says it "implements" Speaker — it just does
}
Full code:
examples/month-01/interfaces/main.go· Run:go run ./examples/month-01/interfaces
🏋️ Exercises / Practice¶
| Exercise | Status | Link |
|---|---|---|
Shape interface with Rectangle/Circle + TotalArea([]Shape) |
✅ | exercises/month-01/week-3/shapes |
Compile-time var _ Shape = ... assertions |
✅ | exercises/month-01/week-3/shapes |
🐛 Mistakes Made¶
- Returned a
(*MyError)(nil)aserrorand the caller'serr != nilfired — the nil-interface trap. Fixed by returning a literalnil. - Made an interface with five methods, then could only satisfy it with one struct. Split it into small interfaces.
❓ Open Questions¶
- When is it worth an explicit
var _ I = (*T)(nil)guard? (Whenever the satisfaction is load-bearing and you want a compile error if it breaks.)
🧠 Active Recall (answer without looking)¶
-
Q: How does a Go type declare that it implements an interface?
A
It doesn't — satisfaction is *implicit*. Having the required method set is enough; there is no `implements` keyword. -
Q: Why can an
errorvalue be non-nil even though it "holds nil"?
A
An interface is a (type, value) pair and is nil only if *both* are nil. A typed nil pointer (e.g. `(*MyError)(nil)`) stored in an `error` sets the type word, so the interface is non-nil — the nil-interface trap.
🪶 Feynman Reflection¶
An interface is a contract written as a checklist of methods. In Go nobody signs the contract — if your type happens to have every method on the list, it is a valid party to the contract automatically. That looseness is what lets unrelated packages fit together like Lego without importing each other.
🕳️ Knowledge Gaps¶
- Reflexes around the nil-interface trap in real error-returning code — drill it in the errors days.
✅ Summary¶
I can define small interfaces, rely on implicit satisfaction to decouple packages, and I know an interface value is a (type, value) pair — which explains the nil-interface trap.
⏭️ Next Steps / Prep for Tomorrow¶
- Day 017: getting the concrete value back out with type assertions and type switches.
| Time spent | Difficulty | Confidence |
|---|---|---|
| 90 min | 🟦🟦⬜⬜⬜ | 🟦🟦🟦⬜⬜ |
Suggested commit: feat(examples): interfaces and implicit satisfaction (day 016)