Reference

Python → Go for coding interviews

You know exactly what you'd write in Python. This is what it is in Go — every container and idiom that comes up in a whiteboard problem, in one place, plus the five things Go does differently that will cost you a bug if nobody warns you.

Why bother writing interview code in Go at all? The short version →

Containers

Go ships a slice and a map. Everything else you build in a few lines or get from slices, sort, and container/heap.

dynamic array
Pythonxs = []
Goxs := []int{}
append
Pythonxs.append(v)
Goxs = append(xs, v)
pop last
Pythonv = xs.pop()
Gov := xs[len(xs)-1]; xs = xs[:len(xs)-1]
slice (copy)
Pythonxs[i:j]
Goslices.Clone(xs[i:j]) // xs[i:j] aliases
hash map
Pythond = {}
God := map[string]int{}
get with default
Pythond.get(k, 0)
God[k] // missing key = zero value
key present?
Pythonk in d
Go_, ok := d[k]
defaultdict(list)
Pythond[k].append(v)
God[k] = append(d[k], v) // nil slice appends fine
Counter
PythonCounter(xs)
Gofor _, x := range xs { cnt[x]++ }
set
Pythons = set()
Gos := map[int]struct{}{}
set add / test
Pythons.add(v); v in s
Gos[v] = struct{}{}; _, ok := s[v]
stack
Pythonlist (append / pop)
Goslice (append / xs[:len-1])
queue
Pythoncollections.deque
Goq = q[1:] // fine for BFS
min-heap
Pythonheapq.heappush / heappop
Gocontainer/heap + 5 methods on your type
max-heap
Pythonpush -v
GoLess(i, j) returns h[i] > h[j]
binary search
Pythonbisect.bisect_left(xs, t)
Gosort.SearchInts(xs, t) / slices.BinarySearch
tuple / record
Python(a, b)
Gostruct{ a, b int } or [2]int
tuple as map key
Pythond[(r, c)]
God[[2]int{r, c}] // arrays compare; slices don't

Idioms

The one-liners. Most have a direct equivalent since Go 1.21/1.22; the arithmetic ones don't, and those are where Python habits bite.

sort with key
Pythonxs.sort(key=lambda p: p[1])
Goslices.SortFunc(xs, func(a, b P) int { return a.y - b.y })
sort ints / strings
Pythonxs.sort()
Goslices.Sort(xs)
reverse
Pythonxs[::-1]
Goslices.Reverse(xs) // in place
enumerate
Pythonfor i, x in enumerate(xs)
Gofor i, x := range xs
range
Pythonfor i in range(n)
Gofor i := range n // Go 1.22+
min / max
Pythonmin(a, b)
Gomin(a, b) // built in since 1.21
infinity
Pythonfloat('inf')
Gomath.MaxInt // ints don't have one
integer division
Python-7 // 2 == -4
Go-7 / 2 == -3 // truncates toward zero
modulo of negatives
Python-7 % 3 == 2
Go-7 % 3 == -1 // ((a % n) + n) % n to fix
string builder
Python"".join(parts)
Govar b strings.Builder; b.WriteString(s)
chars of a string
Pythonfor ch in s
Gofor _, r := range s // runes; s[i] is a BYTE
string → mutable
Pythonlist(s)
Go[]byte(s) (ASCII) / []rune(s) (Unicode)
swap
Pythona, b = b, a
Goa, b = b, a
memoise
Python@functools.lru_cache
Gomemo := map[key]int{} + a closure
recursion depth
Pythonsys.setrecursionlimit(…)
Gonot needed — goroutine stacks grow to 1 GB

Five traps for Python developers

None of these are exotic. Each one is a wrong answer that compiles.

1. Slices alias

GO
1row := grid[2] // a view, not a copy
2row[0] = 9 // grid[2][0] is now 9
3
4a := []int{1, 2, 3, 4}
5b := a[:2] // shares a's backing array
6b = append(b, 99) // overwrites a[2]! a is now [1 2 99 4]

A slice is a pointer, a length and a capacity into a shared array. Reslicing shares; only an append that exceeds capacity copies. Python's xs[i:j] always copies, so this is the trap you'll hit first. Use slices.Clone or the three-index form xs[i:j:j] when you need a private copy.

2. The range variable is a copy

GO
1for _, p := range points {
2 p.x++ // p is a COPY; points is unchanged
3}
4for i := range points {
5 points[i].x++ // index to mutate
6}

for _, p := range hands you a copy of each element. Mutating it does nothing to the slice. Index in, or range over a slice of pointers.

3. A nil map panics on write

GO
1var m map[string]int // nil
2m["a"] = 1 // panic: assignment to entry in nil map
3m := map[string]int{} // or make(map[string]int)

Reading a nil map is fine (you get zero values). Writing panics. A var m map[K]V declaration is the usual culprit — use a literal or make.

4. Map iteration order is random

GO
1for k, v := range m { … } // order is RANDOMISED per run
2// Need order? Collect the keys, slices.Sort them, then range.

Python dicts keep insertion order; Go maps deliberately don't, and the order changes between runs. Any algorithm whose output depends on iteration order is nondeterministic. Sort the keys when it matters.

5. Call heap.Push, not h.Push

GO
1h := &IntHeap{5, 2, 8}
2heap.Init(h)
3heap.Push(h, 1) // heap.Push, never h.Push
4x := heap.Pop(h).(int) // heap.Pop, never h.Pop

The five methods you write (Len, Less, Swap, Push, Pop) are the interface; the package functions do the sifting. Calling your own Push appends without restoring the heap property and nothing warns you.

Two things that aren't in the table

  • Integer overflow is real. Python ints are unbounded; Go's int is 64 bits. Write lo + (hi-lo)/2 for a midpoint, and reach for math/big only when a problem truly needs it.
  • There's no set type, and that's fine. map[T]struct{} is the idiom. It costs one line of wrapper if you want Add/Has — generics make that wrapper reusable.
Go deeper

The table gets you writing Go. The course gets you fluent.

Module 03 of Reach For builds every one of these containers from scratch with step-through diagrams of what the memory is doing, then the remaining modules use them for the actual patterns — with exercises, reference solutions, and a grading prompt for each.