Go Cheatsheet
Control Flow
Use this Go reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
if / else
No parentheses around the condition; braces are mandatory.
if x > 0 { fmt.Println("positive") } else if x < 0 { fmt.Println("negative") } else { fmt.Println("zero") }
Initialization statement
A short statement before the condition; the variable is scoped to the if/else block.
if v, ok := m[key]; ok { fmt.Println("found:", v) } else { fmt.Println("not found") } if err := doSomething(); err != nil { return err }
for — the only loop
Go has one loop keyword: for. It covers all looping patterns.
C-style
for i := 0; i < 10; i++ { fmt.Println(i) }
While-style
n := 1 for n < 100 { n *= 2 }
Infinite loop
for { // break to exit }
Range loop
Iterates over arrays, slices, maps, strings, and channels.
nums := []int{10, 20, 30} for i, v := range nums { // index + value fmt.Println(i, v) } for i := range nums { // index only } for _, v := range nums { // value only } // string: iterates over runes for i, r := range "héllo" { fmt.Printf("%d %c\n", i, r) } // map m := map[string]int{"a": 1, "b": 2} for k, v := range m { fmt.Println(k, v) } // channel — reads until closed for msg := range ch { fmt.Println(msg) }
break and continue
for i := 0; i < 10; i++ { if i == 3 { continue // skip to next iteration } if i == 7 { break // exit loop } fmt.Println(i) }
Labeled break / continue
outer: for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { if i+j == 3 { break outer // exits the outer loop } } }
switch
No fall-through by default; no break needed. Cases can hold multiple values.
switch x { case 1: fmt.Println("one") case 2, 3: fmt.Println("two or three") default: fmt.Println("other") }
Initializer statement
switch os := runtime.GOOS; os { case "linux": fmt.Println("Linux") case "darwin": fmt.Println("macOS") }
Expression-less switch (replaces if-else chains)
switch { case x < 0: fmt.Println("negative") case x == 0: fmt.Println("zero") default: fmt.Println("positive") }
Type switch
Used to branch on an interface's dynamic type.
func describe(i interface{}) string { switch v := i.(type) { case int: return fmt.Sprintf("int: %d", v) case string: return fmt.Sprintf("string: %s", v) case bool: return fmt.Sprintf("bool: %t", v) default: return fmt.Sprintf("unknown: %T", v) } }
fallthrough
Explicitly falls through to the next case (transfers control, not condition re-evaluated).
switch x { case 1: fmt.Println("one") fallthrough case 2: fmt.Println("two or continued from one") }
goto
Jumps to a labeled statement within the same function. Rarely used; cannot jump over variable declarations.
i := 0 loop: if i < 5 { fmt.Println(i) i++ goto loop }
defer
A deferred call runs when the surrounding function returns (LIFO order). Arguments are evaluated immediately.
func readFile(path string) error { f, err := os.Open(path) if err != nil { return err } defer f.Close() // runs when readFile returns // use f... return nil }
Multiple defers (LIFO)
func demo() { defer fmt.Println("third") defer fmt.Println("second") defer fmt.Println("first") } // prints: first, second, third
Defer with a closure (captures variables by reference)
func double(x int) (result int) { defer func() { result *= 2 // modifies named return value }() result = x return }
panic and recover
panic unwinds the call stack; recover can catch it inside a deferred function.
func safeDiv(a, b int) (result int, err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("recovered: %v", r) } }() result = a / b // panics if b == 0 return } // panic with a value panic("something went wrong") panic(errors.New("bad state"))
Use
panic/recoveronly for truly exceptional situations; prefer returningerrorfor normal error handling.
select
Waits on multiple channel operations; picks one that is ready (random if multiple are ready).
select { case msg := <-ch1: fmt.Println("ch1:", msg) case msg := <-ch2: fmt.Println("ch2:", msg) case ch3 <- "value": fmt.Println("sent to ch3") default: fmt.Println("no channel ready") // non-blocking }
Timeout pattern
select { case result := <-work: fmt.Println(result) case <-time.After(2 * time.Second): fmt.Println("timed out") }
Loop until done
for { select { case v := <-data: process(v) case <-done: return } }