THN Interview Prep

Go Idioms — DSA & Interview Snippets

Short, runnable patterns. No single-letter names in examples.

Slices vs arrays and make

Array: fixed length, value type (copied on assignment). Slice: descriptor (ptr, len, cap) over a backing array. Preallocate capacity with make when size is known to limit reallocations.

package main

import "fmt"

func main() {
	fixed := [3]int{1, 2, 3}
	buffer := make([]int, 0, 8)
	buffer = append(buffer, 1, 2, 3, 4)
	fmt.Println(fixed, len(buffer), cap(buffer))
}

append pitfalls

append may allocate a new backing array; holding references to old slice headers after heavy append can show stale views.

package main

import "fmt"

func main() {
	first := []int{1, 2}
	second := append(first, 3)
	first[0] = 9
	fmt.Println(first, second)
}

Shared backing: mutating indices still visible through aliases until append grows past cap.

Map iteration order is undefined

package main

import "fmt"

func main() {
	scores := map[string]int{"ada": 10, "bob": 20}
	for name := range scores {
		fmt.Println(name)
	}
}

Do not rely on order; sort keys explicitly if order matters.

defer gotchas

defer runs LIFO at function return; arguments evaluated immediately (not at defer time).

package main

import "fmt"

func main() {
	value := 1
	defer fmt.Println(value)
	value = 2
	fmt.Println("done")
}

Loop + defer inside the loop defers until function exit (often wrong for cleanup—use a local function or avoid defer in tight loops).

Error wrapping (fmt.Errorf + %w)

package main

import (
	"errors"
	"fmt"
)

func main() {
	inner := errors.New("root cause")
	wrapped := fmt.Errorf("context: %w", inner)
	fmt.Println(errors.Is(wrapped, inner))
}

Use %w for unwrap / errors.Is / errors.As chains.

Mutex vs channels (rule of thumb)

Protect shared memory with sync.Mutex (or RWMutex). Channels orchestrate goroutines and pass ownership—do not replace every mutex with a channel “for idiomatic Go.”

package main

import (
	"fmt"
	"sync"
)

func main() {
	var mutex sync.Mutex
	total := 0
	var wait sync.WaitGroup
	for offset := 0; offset < 3; offset++ {
		wait.Add(1)
		go func(delta int) {
			defer wait.Done()
			mutex.Lock()
			total += delta
			mutex.Unlock()
		}(offset)
	}
	wait.Wait()
	fmt.Println(total)
}

container/heap interface checklist

Implement heap.Interface: Len, Less, Swap, Push(x any), Pop() any. After mutations, call heap.Fix, heap.Push, or heap.Pop—not only raw slice edits.

package main

import (
	"container/heap"
	"fmt"
)

type IntHeap []int

func (receiver IntHeap) Len() int           { return len(receiver) }
func (receiver IntHeap) Less(left, right int) bool { return receiver[left] < receiver[right] }
func (receiver IntHeap) Swap(left, right int)      { receiver[left], receiver[right] = receiver[right], receiver[left] }

func (receiver *IntHeap) Push(value any) { *receiver = append(*receiver, value.(int)) }
func (receiver *IntHeap) Pop() any {
	old := *receiver
	last := old[len(old)-1]
	*receiver = old[:len(old)-1]
	return last
}

func main() {
	values := &IntHeap{3, 1, 2}
	heap.Init(values)
	heap.Push(values, 0)
	fmt.Println(heap.Pop(values))
}

strings.Builder

package main

import (
	"fmt"
	"strings"
)

func main() {
	var builder strings.Builder
	parts := []string{"a", "b", "c"}
	for _, part := range parts {
		builder.WriteString(part)
	}
	fmt.Println(builder.String())
}

Avoid repeated string concatenation in hot loops.

Runes vs bytes (Unicode)

Bytes: UTF-8 raw; runes: Unicode code points. Indexing a string byte-wise can split multibyte characters.

package main

import "fmt"

func main() {
	text := "éclair"
	fmt.Println(len(text), len([]rune(text)))
	for _, runeValue := range text {
		fmt.Printf("%U ", runeValue)
	}
}

sort.Slice

package main

import (
	"fmt"
	"sort"
)

func main() {
	pairs := [][]int{{2, 9}, {1, 5}, {3, 4}}
	sort.Slice(pairs, func(left, right int) bool {
		return pairs[left][0] < pairs[right][0]
	})
	fmt.Println(pairs)
}

Idiomatic index loops

package main

import "fmt"

func main() {
	items := []string{"x", "y"}
	for index := range items {
		fmt.Println(index, items[index])
	}
	for index, value := range items {
		fmt.Println(index, value)
	}
}

See also: Data structure operations.

Last updated on

Spotted something unclear or wrong on this page?

On this page