Mutating Methods
A type whose methods change its own state is the most ordinary thing in Go, and it comes down to two decisions: which receiver each method takes, and whether the zero value works without a constructor.
The zero value first
Stack holds a []int. Appending to a nil slice allocates one, so the zero
Stack is already an empty stack:
var s Stack
s.Push(1) // fine, no constructor
That is worth designing for. bytes.Buffer, strings.Builder and sync.Mutex
all work this way, and it is why you rarely see a New function in the standard
library.
Tally holds a map[string]int, and a map does not have that property.
Reading a nil map is fine and gives the zero value; writing to one
panics with "assignment to entry in nil map". So a type with a map field either
needs a constructor, or needs its writing methods to make the map first. This
challenge does both: NewTally exists, and Add still copes with a Tally
someone built as var t Tally.
Which receiver
Anything that assigns to a field needs a pointer receiver. Push assigns to
s.items - append may return a different slice header - so Push is
func (s *Stack) Push(...). Len only reads, so it can be a value receiver,
and the copy that makes is cheap: copying a struct with a slice field copies the
three-word header, not the elements.
The usual advice is to pick one receiver kind per type and stay with it, so a reader does not have to check each method. The mixture here is deliberate, to make the difference visible.
The slice trap, again
DrainAll takes a []Stack and has to empty each one. Ranging over values
hands out copies, and draining a copy leaves the original full:
for _, s := range stacks { s.Drain() } // drains nothing
for i := range stacks { stacks[i].Drain() } // drains the slice
Task
Fill in main.go: the Stack methods, DrainAll, and the Tally methods.
Hints
Popremoves by reslicing: reads.items[len(s.items)-1], then assigns.items = s.items[:len(s.items)-1]. Check the length first, or the index panics.Draincan loop onPopuntil it returns false. Start from[]int{}so a drained empty stack gives[]rather than nil.PushAllis variadic:vsis a[]int, andPushAll()gives it length 0.AddandMergewrite to the map, so both need theif t.counts == nil { t.counts = map[string]int{} }guard.CountandTotalonly read, and reading a nil map needs no guard at all.Mergemust not touchother, so read fromother.countsand write only tot.counts. It is also called with nil, so check for that first.