Lesson 9 · 9 of 20
A generic stack · Go
Build a generic Stack[T] type in Go with Push, Pop and Len, and use it with strings and with integers: an editor's undo history.

Types can have type parameters too. `Stack[T any]` is a stack of items of type T, kept in a slice. Stack[string] and Stack[int] are two different types, but both come from one piece of code, with no copying and no any.
Methods repeat the parameter in the receiver: func (s *Stack[T]) Push(v T). The receiver is a pointer because Push and Pop change s.items; with a value receiver they would change a copy. One detail: in Go, methods cannot declare type parameters of their own, only use the type's.
Pop removes the last item (a stack is LIFO: last in, first out) and returns two values, the item and a bool. When the stack is empty it returns top untouched, which is the zero value of T ("" for strings, 0 for numbers), and false. That is why the fourth undo prints undo "" ok=false.
var history Stack[string] is ready to use as it is: its slice is nil, and append works on it. The edits are undone in the reverse order they were made, which is exactly what you expect from an undo button.
pages uses the same stack with integers, like a browser's history. &Stack[int]{items: ...} creates it with initial data; that works here because items is visible inside package main, even though it starts with a lowercase letter.
Code · main.go
package mainimport "fmt"// Stack is a generic type: Stack[string], Stack[int]... one implementation.type Stack[T any] struct{ items []T }func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }func (s *Stack[T]) Len() int { return len(s.items) }// Pop returns the zero value of T and false when the stack is empty.func (s *Stack[T]) Pop() (top T, ok bool) { if len(s.items) == 0 { return top, false // top still holds the zero value of T } top = s.items[len(s.items)-1] s.items = s.items[:len(s.items)-1] return top, true}func main() { var history Stack[string] // the zero value is ready to use for _, edit := range []string{"type 'Dear Ana'", "bold title", "insert logo"} { history.Push(edit) } fmt.Println("edits in history:", history.Len()) for i := 0; i < 4; i++ { edit, ok := history.Pop() fmt.Printf("undo %q ok=%t\n", edit, ok) } pages := &Stack[int]{items: []int{1, 4, 9}} // the same code, now with ints page, _ := pages.Pop() fmt.Println("back from page", page, "| pages left:", pages.Len())}Output · go run main.go
edits in history: 3 undo "insert logo" ok=true undo "bold title" ok=true undo "type 'Dear Ana'" ok=true undo "" ok=false back from page 9 | pages left: 2
Key points
- A generic type is declared with
type Stack[T any] struct. - Methods use the parameter in the receiver:
(s *Stack[T]). var zero Tor a named result gives the zero value ofT.- Returning
(T, bool)avoids magic values on an empty stack. Stack[string]andStack[int]are different types from the same code.
Exercise
Add a Peek() (T, bool) method that returns the top item without removing it, and use it to print which edit would be undone next before each Pop.