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

Blocks and Let Bindings

Blocks are sequences of statements enclosed in braces that evaluate to a value. They provide scoped computation with intermediate bindings, making complex expressions readable and composable.

Block Syntax

A block contains zero or more let bindings followed by a final expression. The block evaluates to the value of the final expression:

{
    let a = 10;
    let b = 20;
    a + b
}

This block evaluates to 30.

Let Bindings

The let keyword introduces a named value within a block. Each binding is terminated by a semicolon:

{
    let price = m_item_price;
    let quantity = m_item_count;
    let subtotal = price * quantity;
    let tax = subtotal * 8 / 100;
    subtotal + tax
}

Bindings are immutable – once assigned, a let variable cannot be reassigned.

Type Annotations

Type annotations on let bindings are optional when the type can be inferred:

let count = 42;              // inferred as integer
let name: String = "hello";  // explicit annotation
let bal: U256 = 0;           // explicit when needed for type clarity

Blocks as Expressions

Because blocks are expressions, they can appear anywhere a value is expected:

In Let Bindings

let fee = {
    let rate = if vip { 1 } else { 3 };
    amount * rate / 100
};

In Member Transforms

Blocks are especially useful in member transforms for multi-step state computations:

m_balances: HashMap<address, U256> {
    in transfer(to, amount) => {
        let from = msg::sender;
        let from_bal = m_balances[from];
        let to_bal = m_balances.get(to).unwrap_or(0);
        m_balances
            .set(from, from_bal - amount)
            .set(to, to_bal + amount)
    }
}

In Pure Functions

Pure function bodies are blocks:

pure fn compute_fee(amount: U256, rate: u64) -> U256 {
    let basis_points = rate as U256;
    let fee = amount * basis_points / 10000;
    fee
}

In If/Else Branches

Each branch of an if/else is a block:

let result = if complex_condition {
    let x = compute_a();
    let y = compute_b();
    x + y
} else {
    let fallback = get_default();
    fallback * 2
};

Scope

Variables declared with let are visible only within their enclosing block. They shadow any outer bindings with the same name:

let x = 10;
let result = {
    let x = 20;  // shadows the outer x
    x + 5        // evaluates to 25
};
// x is still 10 here

Blocks in Route Actions

Inside route action lists, let bindings can be used to compute intermediate values:

routes {
    swap(amount: U256) => [
        let fee = amount * 3 / 100;
        let net = amount - fee;
        Transfer(net) ~> m_recipient,
        Transfer(fee) ~> m_treasury
    ]
}

The semicolon after let distinguishes bindings from actions, which are separated by commas.