Go interview questions

15 programming language interview questions on Go, each with the correct answer and a full explanation.

With Go 1.22 or later, what does this print, given the goroutines are awaited?
for i := 0; i < 3; i++ {
    go func() { fmt.Print(i) }()
}
Show answer

Answer

0, 1 and 2 in some order
Since Go 1.22 each iteration has its own i, so each closure sees its own value. Before 1.22 they shared one variable and often printed 3s. Goroutine order is not guaranteed.

Source: Fixing for loops in Go 1.22

What happens?
var m map[string]int
fmt.Println(m["a"])
m["a"] = 1
Show answer

Answer

It prints 0, then panics on the write
Reading a nil map returns the zero value. Writing panics with assignment to entry in nil map. Create it with make(map[string]int).

Source: The Go Programming Language Specification

What does this print?
a := []int{1, 2, 3}
b := a[:2]
b = append(b, 9)
fmt.Println(a)
Show answer

Answer

[1 2 9]
b shares a's array and has capacity 3, so append writes into a[2] without reallocating. Use a full slice expression a[:2:2] or copy to avoid the aliasing.

Source: Go slices: usage and internals

What does this print?
type MyErr struct{}
func (*MyErr) Error() string { return "x" }

var p *MyErr = nil
var err error = p
fmt.Println(err != nil)
Show answer

Answer

true
An interface is nil only when both its type and value are nil. err holds type *MyErr with a nil value, so it is not nil. Return a literal nil for no error.

Source: Go FAQ

What does this print?
for i := 0; i < 3; i++ {
    defer fmt.Print(i)
}
Show answer

Answer

210
Deferred calls run last in, first out when the function returns, and their arguments are evaluated when defer runs.

Source: The Go Programming Language Specification, Effective Go

What does this print when the function returns?
x := 1
defer fmt.Println(x)
x = 2
Show answer

Answer

1
Arguments to a deferred call are evaluated immediately. To see the final value, defer a closure: defer func() { fmt.Println(x) }().

Source: The Go Programming Language Specification

What happens when main runs this?
ch := make(chan int)
ch <- 1
fmt.Println(<-ch)
Show answer

Answer

fatal error: all goroutines are asleep, deadlock
A send on an unbuffered channel blocks until a receiver is ready. The only goroutine is blocked on the send, so the runtime reports a deadlock. Use a buffer of 1 or receive in another goroutine.

Source: The Go Programming Language Specification

What does this print?
ch := make(chan int, 1)
close(ch)
v, ok := <-ch
fmt.Println(v, ok)
Show answer

Answer

0 false
Receiving from a closed, drained channel returns the zero value immediately with ok false. Sending on a closed channel is what panics.

Source: The Go Programming Language Specification

What does this print?
type Counter struct{ n int }
func (c Counter) Inc() { c.n++ }

c := Counter{}
c.Inc()
fmt.Println(c.n)
Show answer

Answer

0
A value receiver gets a copy, so the increment is lost. Use func (c *Counter) Inc() to modify the caller's value.

Source: Effective Go

In Go, what does len("héllo") return? (é is the single code point U+00E9.)
Show answer

Answer

6
In Go, len on a string counts bytes. Go source is UTF-8, and é (U+00E9) takes 2 bytes. Use utf8.RuneCountInString to count characters.

Source: The Go Programming Language Specification

Several goroutines write to a plain Go map with no locking. What can happen?
Show answer

Answer

The runtime may crash with fatal error: concurrent map writes
Built in maps are not safe for concurrent writes, and the runtime detects some races and aborts. Guard the map with a sync.Mutex, or use sync.Map for its specific use cases.

Source: Go FAQ

What is the bug?
var wg sync.WaitGroup
for _, j := range jobs {
    go func() {
        wg.Add(1)
        defer wg.Done()
        run(j)
    }()
}
wg.Wait()
Show answer

Answer

Wait can return before Add runs, so jobs may not be awaited
Add must happen before the goroutine starts, otherwise Wait may see a zero counter and return early. Call wg.Add(1) before go, or wg.Add(len(jobs)) once. Since Go 1.22 j is per iteration.

Source: The Go Memory Model

In a Go select statement, two cases are ready at the same time. Which runs?
Show answer

Answer

One chosen pseudo randomly
When several cases can proceed, Go picks one by uniform pseudo random selection, which prevents starvation. Do not rely on case order for priority.

Source: The Go Programming Language Specification

In Go, a handler starts a goroutine that sends its result on an unbuffered channel, and returns early on context timeout. What goes wrong?
Show answer

Answer

The goroutine blocks on the send forever, leaking
Nobody receives after the timeout, so the send blocks forever. Make the channel buffered with capacity 1, or select on ctx.Done() in the sender. Cancellation is cooperative.

Source: The Go Memory Model

In Go, map[string]int, func(), []int and [2]int: name the only type here that can be used as a map key.
Show answer

Answer

[2]int
Map keys must be comparable. Arrays of comparable types are, while slices, maps and functions are not.

Source: The Go Programming Language Specification

These questions come from the Language Gotchas pack in Bertopia.

More programming language topics

Practise these on your commute