Output-Based Questions
TL;DR
Output-based questions give you a short snippet of Go code and ask: “What does this print? Does it compile? Does it panic?” These tests your knowledge of slices, maps, defers, and interface mechanics.
Question 1: Slice Re-slicing and Capacity
package main
import "fmt"
func main() {
a := []int{1, 2, 3, 4, 5}
b := a[1:3]
b[0] = 99
b = append(b, 88)
fmt.Println(a)
}
Answer: [1, 99, 3, 88, 5]
Why: b := a[1:3] creates a slice [2, 3] pointing to the exact same underlying array as a.
Setting b[0] = 99 changes the 2 to 99 in the shared array.
b currently has a length of 2, but a capacity of 4 (because it looks at the remaining space in a). So when we do append(b, 88), it does NOT create a new array. It simply overwrites the next slot in the existing array (replacing 4 with 88).
Question 2: Defer Evaluation
package main
import "fmt"
func main() {
i := 10
defer fmt.Println("Deferred:", i)
i = 20
fmt.Println("Normal:", i)
}
Answer:
Normal: 20
Deferred: 10
Why: The arguments to a deferred function are evaluated immediately when the defer statement is executed, not when the function actually runs. At the moment defer was called, i was 10.
Question 3: Maps and Pointers
package main
import "fmt"
type User struct {
Name string
}
func main() {
m := map[int]User{
1: {"Alice"},
}
m[1].Name = "Bob"
fmt.Println(m[1].Name)
}
Answer: Compilation Error! (cannot assign to struct field m[1].Name in map)
Why: Values stored in a Go map are not addressable. You cannot modify a field of a struct directly inside a map. You must either extract the whole struct, modify it, and put it back (user := m[1]; user.Name = "Bob"; m[1] = user), OR change the map to store pointers (map[int]*User).
Question 4: The Nil Interface Trap
package main
import "fmt"
type CustomError struct{}
func (c *CustomError) Error() string { return "Failed" }
func doWork() error {
var err *CustomError = nil
return err
}
func main() {
err := doWork()
if err != nil {
fmt.Println("Error occurred!")
} else {
fmt.Println("All good!")
}
}
Answer: Error occurred!
Why: This is the most famous trap in Go. An interface (like error) is internally a struct with two fields: [Type, Value]. An interface is ONLY nil if BOTH fields are nil.
In doWork(), we return an untyped interface that contains [Type: *CustomError, Value: nil]. Because the Type field is populated, the interface itself is NOT nil!
To fix this, doWork() should explicitly return nil.
Question 5: Goroutine Loop Variable Capture (Pre Go 1.22)
(Note: Go 1.22 fixed this behavior, but interviewers still ask it to test legacy knowledge).
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
words := []string{"A", "B", "C"}
for _, w := range words {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Print(w)
}()
}
wg.Wait()
}
Answer (Pre-1.22): CCC (Usually).
Why: In older Go versions, the loop variable w was a single memory address that was reused on each iteration. By the time the goroutines actually spun up and ran, the loop had already finished, and the value sitting at memory address w was the final element, “C”. All three goroutines read from the same address.
(In Go 1.22+, the output will be A, B, C in random order, because w is now instantiated locally inside each loop iteration).