Control flow#
Go keeps the control-flow surface small. if / else,
for (the only loop keyword), switch, select for
channels, plus break / continue / return /
goto / fallthrough and defer for cleanup-on-exit.
No exceptions; errors are values (see Errors).
For the values branches consider, see Types.
Conditional#
if / else if / else evaluates branches
top-to-bottom. The optional init statement binds a variable
scoped to the conditional.
flowchart TD
A([start]) --> B{x > 0?}
B -->|true| C[positive]
B -->|false| D{x == 0?}
D -->|true| E[zero]
D -->|false| F[negative]
C --> Z([end])
E --> Z
F --> Z
if x > 0 {
positive()
} else if x == 0 {
zero()
} else {
negative()
}
Init form binds a variable scoped to the conditional.
if v, err := load(); err != nil {
return err
} else {
use(v)
}
Go has no ternary expression.
Loops#
for is the only loop keyword. Three forms.
flowchart TD
A([init]) --> B{cond?}
B -->|true| C[body]
C --> D[step]
D --> B
B -->|false| E([end])
C-style#
for i := 0; i < 10; i++ {
fmt.Println(i)
}
While-style#
for queue.Len() > 0 {
handle(queue.Pop())
}
Infinite#
for {
select {
case <-done:
return
case v := <-ch:
handle(v)
}
}
Range#
range walks slices, arrays, maps, strings, channels, and
(Go 1.22+) integers.
Slice or array; binds index and value.
for i, v := range xs {
fmt.Println(i, v)
}
Map; the iteration order is randomised.
for k, v := range m {
fmt.Println(k, v)
}
String; binds byte index and rune.
for i, r := range "héllo" {
fmt.Println(i, r)
}
Channel; runs until the channel is closed.
for v := range ch {
fmt.Println(v)
}
Integer range (Go 1.22+).
for i := range 10 {
fmt.Println(i)
}
For Go 1.22+, the per-iteration loop variable is per iteration (new binding each loop); earlier versions reused the same binding, which bit closures over goroutines.
Switch#
switch is the operator’s branch-table form. Cases do not
fall through by default (the opposite of C); use fallthrough
explicitly when needed.
flowchart TD
A([switch state]) --> B{Idle?}
B -->|yes| C[start]
B -->|no| D{Running?}
D -->|yes| E[step]
D -->|no| F{Done?}
F -->|yes| G[cleanup]
F -->|no| H[default]
C --> Z([end])
E --> Z
G --> Z
H --> Z
switch state {
case Idle:
start()
case Running:
step()
case Done:
cleanup()
default:
return fmt.Errorf("unknown state: %v", state)
}
A switch without an expression is equivalent to
switch true; cases are full boolean expressions.
switch {
case x < 0:
negative()
case x == 0:
zero()
default:
positive()
}
Type switch on an interface value.
switch v := x.(type) {
case int: fmt.Println("int", v)
case string: fmt.Println("string", v)
case nil: fmt.Println("nil")
default: fmt.Println("other", v)
}
Select#
select waits on multiple channel operations. The first
ready case runs; if several are ready, one is picked at random.
select {
case v := <-ch:
handle(v)
case ch <- next:
sent()
case <-ctx.Done():
return ctx.Err()
case <-time.After(5 * time.Second):
return errors.New("timeout")
default:
// runs if no channel is ready (non-blocking)
}
See Concurrency for the goroutine model around
select.
Jumps#
Keyword |
Effect |
|---|---|
|
Exit the innermost |
|
Skip to the next iteration of the innermost |
|
Exit the current function, optionally with return values. |
|
Jump to a named label in the same function. Rare; the common use is to exit cleanly from deeply nested loops. |
|
In |
outer: for _, row := range rows {
for _, cell := range row {
if cell == target { break outer }
}
}
defer#
defer schedules a function call to run when the surrounding
function returns. Deferred calls run in LIFO order. The
operator uses defer for cleanup that must run on every exit
path (close files, unlock mutexes, end traces).
f, err := os.Open(path)
if err != nil { return err }
defer f.Close()
mu.Lock()
defer mu.Unlock()
// … operations on f and the locked resource …
return nil
Deferred arguments are evaluated at the defer statement, not
at exit; closures capture by reference, so deferring a function
that reads a variable sees the latest value.
References#
Syntax for the lexical surface
if/for/switchlive in.Types for truthiness (Go uses
bool, not truthiness) and comparable types.Errors for the error-as-value pattern that replaces exceptions.
Concurrency for
select, channels, and goroutines.