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() // ?
Solution
**Bug**: Both A and B have method M; ambiguous. Compile error: `ambiguous selector c.M`. **Fix** โ be explicit: Or define `M` on C to disambiguate: **Lesson**: Embedded pointer fields with overlapping methods need explicit selection.Bug 10 ๐ด โ Defensive Copy Missing¶
type Buffer struct{ data []int }
func New(initial []int) *Buffer {
return &Buffer{data: initial} // BUG: aliases caller
}
func main() {
src := []int{1, 2, 3}
b := New(src)
src[0] = 99
fmt.Println(b.data) // expected [1 2 3]
}
Solution
**Bug**: `b.data` aliases `src`. Mutating src affects b. **Fix** โ defensive copy: **Lesson**: Constructors taking slices/maps should defensively copy if the caller's data shouldn't be aliased.In this topic
Modes