Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Control Flow

if / else

if temp_c > 80 {
    fan.high()
} else if temp_c > 60 {
    fan.medium()
} else {
    fan.low()
}

No parentheses around conditions. Braces are always required.

if as an Expression

if can produce a value when both branches are present:

// Postfix form: for short single-expression cases
let label = "hot" if temp > 80 else "ok"

// Block form: when branches need statements
let delay_ms = if fast_mode {
    let base = 10
    base
} else {
    let base = 100
    base
}

Both branches must produce the same type. The expression if always requires an else.

if let

Lightweight alternative to match when you only care about one pattern:

if let Some(port) = config.get("port") {
    log.info(port)
} else {
    log.info("using default: 8080")
}

if let Ok(data) = file.read() {
    process(data)
}

Works with Option, Result, and enum variants.

If you only care whether an Option has a value and do not need the inner binding, prefer is_some() or is_none().

If you only care whether a Result succeeded and do not need the success value, prefer is_ok() or is_err().

for

for iterates over collections, ranges, or any iterable value using the in keyword.

Collection iteration: iterate over every element in a list, array, or slice:

for item in items {
    process(item)
}

Half-open range: iterates from the start up to but not including the end:

for i in 0..10 { // 0, 1, 2, ..., 9
    sum += i
}

Inclusive range: iterates from start through and including the end:

for i in 0..=10 { // 0, 1, 2, ..., 10
    sum += i
}

Discard the loop variable: use _ when you only need the iteration count:

for _ in 0..4 {
    pulse()
}

Map iteration uses explicit view methods:

for key in settings.keys() { ... }
for value in settings.values() { ... }
for entry in settings.entries() {
    log.info(entry.key)
    apply(entry.value)
}

while

while not done {
    step()
}

while bytes_read < target {
    bytes_read += port.read(buf)?
}

loop

An explicit infinite loop. Used for MCU main loops and spin-wait patterns:

fn main() -> never {
    loop {
        let event = wait_for_event()
        handle(event)
    }
}

break and continue

for item in items {
    if item == 0 {
        continue     // skip zeros
    }
    if item > MAX {
        break        // stop at first oversize item
    }
    process(item)
}

match

Covered in detail in the Match chapter. Brief overview:

match status {
    Status.Ok => run()
    Status.Pending => wait()
    Status.Failed(e) => log.error(e)
}

match must be exhaustive. Every possible variant must be covered.

Operators and Precedence

Full precedence table from highest to lowest:

LevelOperators
Postfix(), [], ., ?
Prefixunary -, not, ~
Multiplicative*, /, %
Additive+, -
Shift<<, >>
Bitwise AND&
Bitwise XOR^
Bitwise OR|
Relational / membership<, <=, >, >=, in
Equality==, !=
Logical ANDand
Logical ORor
Conditionala if cond else b
Block if expressionif cond { ... } else { ... }
match expression

Assignment is always a statement, never an expression.

Standalone Blocks

Plain { ... } blocks create a new lexical scope. Useful for defer-scoped cleanup:

{
    let guard = mutex.lock()
    defer guard.release()
    update_shared()
}
// guard released here

do_other_work()

return, break, and continue inside a block still target the enclosing function or loop.