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.