HowtoGo
Data Structures

Stack

A stack is last-in-first-out: the most recently pushed value is the first one popped. Go has no built-in Stack type; a slice with Push/Pop methods is the idiomatic way to build one.

The rule

Last in, first out

Values come off in the reverse of the order they went on.

  • Push: add a value to the top.
  • Pop: take the top value off and hand it back.
  • Peek: look at the top value without removing it.
Interactive stack: push, pop, and peek
on the stack top (just changed)

A stack is last-in-first-out: Push adds to the top, Pop removes from the top, Peek reads the top without removing it. Try it below.

Purpose

Hold values while the only one you need back is the most recent.

Input

Values pushed one at a time onto a []int.

Output

The most recently pushed value, handed back by Pop.

Constraints

  • Popping an empty stack has to be guarded, or the index goes negative.
  • Only the top is reachable. Getting at the middle means popping down to it.
  • The backing array is kept after a pop, so a stack that spiked once stays large.

Push appends to the end of the slice. Pop reads and removes that same end, the top of the stack; Peek reads it without removing it. Both report false instead of panicking when the stack is empty.

type Stack []int

func (s *Stack) Push(v int) {
    *s = append(*s, v)
}

func (s *Stack) Pop() (int, bool) {
    if len(*s) == 0 {
        return 0, false
    }
    v := (*s)[len(*s)-1]
    *s = (*s)[:len(*s)-1]
    return v, true
}

func (s Stack) Peek() (int, bool) {
    if len(s) == 0 {
        return 0, false
    }
    return s[len(s)-1], true
}

Pushing 1, 2, 3 and popping once removes 3, leaving [1 2] behind. Pop always removes from the end, never the front.

var s Stack
s.Push(1)
s.Push(2)
s.Push(3)

v, _ := s.Pop()
fmt.Println(v)
fmt.Println(s)

Stack operations

OperationBehavior
Push(v)Appends v to the end of the slice, the top of the stack.
Pop()Removes and returns the last element. ok is false if the stack is empty.
Peek()Reads the last element without removing it.
len(s)Number of elements currently on the stack.

Growth

The whole program main.go Show
package main

import "fmt"

type Stack []int

func (s *Stack) Push(v int) { *s = append(*s, v) }

func (s *Stack) Pop() (int, bool) {
    if len(*s) == 0 {
        return 0, false
    }
    v := (*s)[len(*s)-1]
    *s = (*s)[:len(*s)-1]
    return v, true
}

func (s Stack) Peek() (int, bool) {
    if len(s) == 0 {
        return 0, false
    }
    return s[len(s)-1], true
}

func main() {
    var s Stack
    s.Push(1)
    s.Push(2)
    s.Push(3)

    v, _ := s.Pop()
    fmt.Println(v)
    fmt.Println(s)
}

A slice-backed stack's Push is amortized O(1): append only reallocates and copies when the backing array runs out of capacity, which happens on a shrinking fraction of calls as the stack grows.

Terminal
$ go run stack.go
3
[1 2]