Go Pointers with Structs — Find the Bug¶
Instructions¶
Identify the bug, explain, fix. Difficulty: 🟢 🟡 🔴.
Bug 1 🟢 — Mixing Receivers Breaks Interface¶
type Animal struct{ Name string }
func (a Animal) Name1() string { return a.Name }
func (a *Animal) Bark() { fmt.Println(a.Name) }
type Speaker interface { Name1() string; Bark() }
var s Speaker = Animal{Name: "Rex"}
Solution
**Bug**: `Animal{}` doesn't satisfy `Speaker` because `Bark` has pointer receiver. Compile error. **Fix**: `var s Speaker = &Animal{Name: "Rex"}` — `*Animal` has both methods. **Lesson**: Pick consistent receiver types. Mixed receivers limit interface satisfaction.Bug 2 🟢 — Nil Struct Field Access¶
Solution
**Bug**: Nil pointer dereference panic. Auto-dereferencing nil panics. **Fix**:Bug 3 🟢 — Map Value Mutation¶
Solution
**Bug**: Map values are not addressable; can't mutate fields directly. Compile error. **Fix** (extract): Or store pointers:Bug 4 🟡 — Returning Slice of Pointers from Loop¶
type Item struct{ V int }
func make() []*Item {
items := []Item{{1}, {2}, {3}}
var ptrs []*Item
for _, it := range items {
ptrs = append(ptrs, &it) // pre-1.22 bug
}
return ptrs
}
(Pre Go 1.22.) What does each pointer point to?
Solution
**Pre-1.22**: All pointers point to the same `it` (last value: `{V: 3}`). **Fix** for pre-1.22: Or shadow with `it := it` (creates per-iter copy). **Lesson**: Pre-1.22 loop variable capture pitfall. Use index for slice element pointers.Bug 5 🟡 — Pointer Receiver Required on Non-Addressable Value¶
Solution
**Bug**: `T{N: 1}` is a composite literal, not addressable. Can't call pointer-receiver method. Compile error. **Fix** — assign to a variable first: Or allocate with pointer:Bug 6 🟡 — Constructor Returns Pointer to Local That's Reused¶
type Builder struct{ Items []int }
var b Builder
func New() *Builder {
b = Builder{Items: []int{}} // reuses package var
return &b
}
Solution
**Bug**: All callers get a pointer to the SAME `b`. Each `New()` overwrites the previous; old "instances" become garbage. **Fix** — fresh allocation each call: **Lesson**: Constructors should allocate fresh per call.Bug 7 🟡 — Storing Pointer to Local¶
type Service struct{ Logger *Logger }
func setup() *Service {
l := Logger{Prefix: "SVC"}
return &Service{Logger: &l}
}
Is this safe?
Solution
**Discussion**: Yes — Go's escape analysis moves `l` to the heap because `&l` escapes. Safe. The compiler verifies; this is idiomatic Go. Verify with `go build -gcflags="-m"`. **Lesson**: Returning pointers to locals is safe in Go (unlike C); escape analysis handles allocation.Bug 8 🔴 — Concurrent Mutation of Pointer Field¶
type Cache struct{ data *Data }
func update(c *Cache, d *Data) { c.data = d }
c := &Cache{}
go update(c, &Data{V: 1})
go update(c, &Data{V: 2})
fmt.Println(c.data)
Solution
**Bug**: Two goroutines write to `c.data` concurrently. Race. Reader may see torn value. **Fix** — atomic.Pointer: Or mutex. **Lesson**: Concurrent pointer-field updates need atomic or mutex.Bug 9 🔴 — Embedded Pointer Method Conflict¶
type A struct{}
func (a *A) M() string { return "A" }
type B struct{}
func (b *B) M() string { return "B" }
type C struct {
*A
*B
}
c := &C{A: &A{}, B: &B{}}
c.M() // ?