Skip to content

Control flow

A block is a sequence of expressions inside braces { ... }. The last expression is the block’s value. Bindings inside are not visible outside.

let value = {
let a = 10
let b = 20
a + bvalue is 30
}
if count > 0 {
fmt.Println("has items")
}
if count > 10 {
fmt.Println("large")
} else {
fmt.Println("small")
}

When both branches are present, if else returns a value. Both branches must produce the same type.

let label = if count > 10 { "large" } else { "small" }
let clamped = if n > max {
max
} else if n < min {
min
} else {
n
}

An if without else has type ().

Runs the body when a pattern matches. Most commonly used with Option.

if let Some(x) = opt {
fmt.Println(x)
}

With else, it works as an expression:

let value = if let Some(x) = opt {
x
} else {
0
}

Iterates over a collection or range, for its side effects.

for item in items {
fmt.Println(item)
}
for i in 0..5 {
fmt.Println(i)prints 0, 1, 2, 3, 4
}

Supported iterables:

IterableElement type
Slice<T>T
Array<T, N>T
Map<K, V>(K, V)
Range<T>, RangeInclusive<T>, RangeFrom<T>T
Channel<T>, Receiver<T>T
EnumeratedSlice<T>(int, T)
iter.Seq<T>T
iter.Seq2<K, V>(K, V)

See Functions for writing an iterator

To iterate over a string, pick a unit:

for r in s.runes() {
fmt.Println(r)
}
for b in s.bytes() {
fmt.Println(b)
}

Maps require destructuring into key and value:

for (name, age) in ages {
fmt.Println(name, age)
}

Use enumerate() for indexed iteration over a slice:

for (i, item) in items.enumerate() {
fmt.Println(i, item)
}

Open-ended ranges start.. loop until a break:

for i in 0.. {
if i >= 10 {
break
}
}

Repeats while a condition is true, for its side effects.

let mut total = 0
while total < 10 {
total += 3
}

Repeats as many times as a pattern matches.

let mut i = 0
while let Some(item) = items.get(i) {
fmt.Println(item)
i += 1
}

An infinite loop. Exit with break.

let mut n = 0
loop {
n += 1
if n == 10 {
break
}
}

A loop has no exit other than break, which can carry a value, so a loop evaluates to the value carried by break.

let mut n = 0
let result = loop {
n += 7
if n > 20 {
break nresult is 21
}
}

A bare break gives the loop type ().

break exits a loop. continue skips to the next iteration of the loop.

for i in 0..100 {
if i % 2 == 0 {
continue
}
if i > 50 {
break
}
fmt.Println(i)
}

There are no labeled breaks. break and continue always apply to the innermost loop.

Returns early from the current function. A bare return returns ().

fn find(items: Slice<int>, target: int) -> Option<int> {
for i in 0..items.length() {
if items[i] == target {
return Some(i)early return
}
}
None
}

Schedules an expression to run when the enclosing function returns, no matter how it returns.

fn file_size(path: string) -> Result<int64, error> {
let file = os.Open(path)?
defer file.Close()runs on function exit, whether file.Stat() succeeds or fails
let info = file.Stat()?
Ok(info.Size())
}

When a function has several defer calls, the last one scheduled runs first.

defer fmt.Println("first")
defer fmt.Println("second")runs before first

A defer block groups cleanup steps that belong together. Inside one block the steps run in the order written, top to bottom.

defer {
conn.flush()runs first
conn.close()runs second
}