DEV Community

Abhishek
Abhishek

Posted on

Implementing a Stack From Scratch

Why I Ended Up Implementing a Stack From Scratch in Go

Recently, during an interview, I was asked a standard question:

Validate a string of parentheses.

I solved it using stack.
But then the interviewer followed up with something unexpected:

“How would you implement a stack from scratch?”

And yeah… my brain froze.

After that, I decided to sit down, revise the fundamentals, and actually build a stack from scratch in Go.

What Exactly Is a Stack?

A stack is one of the simplest data structures.
It follows a very straightforward rule:

LIFO - Last In, First Out

A good analogy is a stack of plates:

  • You place plates on top (Push)
  • You remove plates from the top (Pop)
  • You never access items from the middle or bottom

The most recently added item is always the first one removed.


Core Operations of a Stack

A stack supports four basic operations:

Push - Add an item to the top
Pop - Remove and return the top item
Peek - Return the top item without removing it
IsEmpty - Check whether the stack contains any elements

These simple operations form the basis of many algorithms.


So here is the implementation.


Stack Implementation in Go (Using Generics)

package main

import "fmt"

func main() {
    var st Stack[int]
    st.Push(10)
    fmt.Println(st.Pop())
    fmt.Println(st.Peek())
    fmt.Println(st.IsEmpty())
    st.Push(71)
    fmt.Println(st.IsEmpty())
}

// stack
type Stack[T any] struct {
    data []T
}

// Push
func (s *Stack[T]) Push(value T) {
    s.data = append(s.data, value)
}

// Pop
func (s *Stack[T]) Pop() (T, bool) {
    var zero T
    if len(s.data) == 0 {
        return zero, false
    }
    value := s.data[len(s.data)-1]
    s.data = s.data[:len(s.data)-1]
    return value, true
}

// Peek
func (s *Stack[T]) Peek() (T, bool) {
    var zero T
    if len(s.data) == 0 {
        return zero, false
    }
    return s.data[len(s.data)-1], true
}

// IsEmpty
func (s *Stack[T]) IsEmpty() bool {
    return len(s.data) == 0
}
Enter fullscreen mode Exit fullscreen mode

type Stack[T any] struct {
    data []T
}
Enter fullscreen mode Exit fullscreen mode

What This Means

  • Stack is a generic type.
  • T represents any type: int, string, rune, struct, etc.
  • Internally, values are stored in a slice ([]T).

Slices naturally fit stack behavior because:

  • append() adds an element to the end (Push)
  • slicing ([:len-1]) removes the last element efficiently (Pop)

1. Push - Add an Item to the Top

func (s *Stack[T]) Push(value T) {
    s.data = append(s.data, value)
}
Enter fullscreen mode Exit fullscreen mode

Push appends the element to the end of the slice, which represents the top of the stack.

Using a pointer receiver (*Stack[T]) ensures the original stack is modified.


2. Pop - Remove and Return the Top Element

func (s *Stack[T]) Pop() (T, bool) {
    var zero T
    if len(s.data) == 0 {
        return zero, false
    }

    value := s.data[len(s.data)-1]
    s.data = s.data[:len(s.data)-1]
    return value, true
}
Enter fullscreen mode Exit fullscreen mode
  • If the stack is empty, Pop returns (zero value, false)
  • Otherwise, it returns the last element and removes it using slicing

zero is the default value of type T (0 for int, "" for string, false for bool, etc.)


3. Peek - View the Top Element Without Removing It

func (s *Stack[T]) Peek() (T, bool) {
    var zero T
    if len(s.data) == 0 {
        return zero, false
    }
    return s.data[len(s.data)-1], true
}
Enter fullscreen mode Exit fullscreen mode

Peek returns the top element but leaves the stack unchanged.


4. IsEmpty - Check Whether the Stack Is Empty

func (s *Stack[T]) IsEmpty() bool {
    return len(s.data) == 0
}
Enter fullscreen mode Exit fullscreen mode

A simple check based on slice length.


Example Usage

var st Stack[int]

st.Push(10)
fmt.Println(st.Pop())      // (10, true)
fmt.Println(st.Peek())     // (0, false)
fmt.Println(st.IsEmpty())  // true

st.Push(71)
fmt.Println(st.IsEmpty())  // false
Enter fullscreen mode Exit fullscreen mode

Summary

This implementation:

  • Uses Go generics
  • Works for any type
  • Is clean, efficient, and easy to understand
  • Follows true LIFO behavior
  • Demonstrates how a stack is built internally

This is a complete, from-scratch stack implementation written in Go using generics.


Top comments (0)