unknwon/the-way-to-go_ZH_CN · error

stack is empty

Error message

stack is empty

What it means

Error returned by Top() in the Chapter 11 generic stack exercise (Stack is a []interface{}). Top() is the non-destructive peek: it returns the last element, and when len(stack) == 0 there is nothing to peek, so instead of indexing into an empty slice it returns (nil, this error). The package teaches the idiomatic Go pattern of returning (value, error) from container accessors rather than panicking.

Source

Thrown at eBook/exercises/chapter_11/stack/stack_general.go:26

func (stack Stack) Len() int {
	return len(stack)
}

func (stack Stack) Cap() int {
	return cap(stack)
}

func (stack Stack) IsEmpty() bool {
	return len(stack) == 0
}

func (stack *Stack) Push(e interface{}) {
	*stack = append(*stack, e)
}

func (stack Stack) Top() (interface{}, error) {
	if len(stack) == 0 {
		return nil, errors.New("stack is empty")
	}
	return stack[len(stack)-1], nil
}

func (stack *Stack) Pop() (interface{}, error) {
	stk := *stack // dereference to a local variable stk
	if len(stk) == 0 {
		return nil, errors.New("stack is empty")
	}
	top := stk[len(stk)-1]
	*stack = stk[:len(stk)-1] // shrink the stack
	return top, nil
}

View on GitHub (pinned to 7a54d34d36)

Solutions

  1. Call stack.IsEmpty() (value receiver, len(stack)==0) before Top() and skip or report empty input when true
  2. Always take both return values: if v, err := s.Top(); err != nil { /* empty */ } else { use v }
  3. If it fires inside a loop, re-check the invariant: the number of Top() calls must never exceed the number of live Push() calls (every Top should correspond to a previously unmatched Push)
  4. Restructure the caller so Top() only runs in a branch that just successfully Push()ed, guaranteeing non-emptiness

Example fix

// before
val := s.Top() // error ignored; val is nil on empty stack

// after
val, err := s.Top()
if err != nil {
	return fmt.Errorf("nothing to read: %v", err)
}
_ = val
Defensive patterns

Strategy: validation

Validate before calling

if s.IsEmpty() {
	return nil // caller-level: nothing to peek
}
v, err := s.Top()
if err != nil {
	return err
}

Try / catch

v, err := s.Top()
if err != nil {
	// stack is empty: skip, report, or terminate this branch
	return err
}
// use v

Prevention

When it happens

Trigger: Calling Top() on a zero-length Stack: a freshly declared stack (var s Stack), one that was never Pushed, or one fully drained because each Pop() shrinks the slice with *stack = stk[:len(stk)-1].

Common situations: Delimiter-matching or expression-evaluation algorithms that peek assuming an opening item remains; loops that read one element past end of input; reusing a stack field across requests without re-checking emptiness; assuming Top() returns zero quietly instead of an error.

Related errors


AI-assisted analysis of unknwon/the-way-to-go_ZH_CN@7a54d34d36 (2026-08-15). Data as JSON: /api/errors/eb6084741ffd6c3e. Report an issue: GitHub.