Do deferred functions run on os.Exit, log.Fatal, runtime.Goexit and panic?
Question 19MediumGo 1.22 to 1.25
- panic: yes. Deferred calls run while the stack unwinds, and that is where
recovercan happen. - runtime.Goexit: yes. It ends the current goroutine and runs its defers.
t.FailNow()in tests uses it. It can't be recovered, and calling it frommaincauses a deadlock crash once all other goroutines finish. - os.Exit: no. The process exits immediately. Defers are skipped, and buffers such as
bufio.Writerare not flushed. - log.Fatal / log.Fatalf: no. They call
os.Exit(1).
func main() {
defer fmt.Println("cleanup") // never printed
if err := run(); err != nil {
log.Fatal(err)
}
}
// Idiomatic: do the work in run(), so its defers run and main is the only place that exits.
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func run() error {
f, err := os.Create("out.txt")
if err != nil {
return err
}
defer f.Close() // always runs
// ...
return nil
}
What the interviewer wants to hear: the run() error pattern, and awareness that log.Fatal in library code is bad practice.
More on Language Fundamentals & Types
- Q17What does this print? (defer with value vs pointer receivers)
- Q18What are the rules for
recover? Why doesn'tdefer recover()stop a panic? - Q20In what order are package-level variables initialized? What does this print?
- Q21How does initialization work across packages, and what are the rules for
init()? - Q22How do labeled
breakandcontinuework? Why does a plainbreakinsideselectnot leave the loop? - Q23What restrictions does Go place on
goto?