Skip to content
This guide targets Fe 26.5. Fe is not production-ready. Install Fe 26.5

Control Flow

Control flow constructs determine the order in which code executes. Fe provides conditionals, pattern matching, and loops, all designed as expressions that can return values.

Use if to execute code based on a condition:

if balance > 0 {
process_withdrawal()
}

Add an else branch for when the condition is false:

if balance >= amount {
withdraw(amount)
} else {
reject_transaction()
}

Chain multiple conditions with else if:

let grade = if score >= 90 {
"A"
} else if score >= 80 {
"B"
} else if score >= 70 {
"C"
} else {
"F"
}

In Fe, if is an expression that returns a value. Both branches must return the same type:

let max = if a > b { a } else { b }
let status = if is_active {
"active"
} else {
"inactive"
}

This is useful for concise conditional assignment:

let fee = if is_premium { 0 } else { calculate_fee(amount) }

The match expression provides powerful pattern matching:

match value {
0 => handle_zero()
1 => handle_one()
_ => handle_other()
}

Like if, match is an expression that returns a value:

let description = match count {
0 => "none"
1 => "one"
2 => "two"
_ => "many"
}

Match on enum variants to handle different cases:

enum Status {
Pending,
Approved,
Rejected,
}
let message: String<31> = match status {
Status::Pending => "Awaiting review"
Status::Approved => "Request approved"
Status::Rejected => "Request rejected"
}

Use patterns to destructure and bind values:

let location = match point {
(0, 0) => "origin"
(0, _) => "on y-axis"
(_, 0) => "on x-axis"
(_, _) => "somewhere else"
}

Match on struct fields:

match user {
User { active: true, role } => process_active(role)
User { active: false, .. } => handle_inactive()
}

Use _ to match any value you don’t need:

match result {
Result::Ok(value) => use_value(v: value)
Result::Err(_) => handle_error() // Ignore the specific error
}

Match expressions must be exhaustive. Every possible value must be handled. The wildcard _ is often used as a catch-all:

let day_name = match day {
1 => "Monday"
2 => "Tuesday"
3 => "Wednesday"
4 => "Thursday"
5 => "Friday"
6 => "Saturday"
7 => "Sunday"
_ => "Invalid day" // Handle all other values
}

Use for to iterate over a range (such as 0..n), an array, a MemVec, a DynArray, or any other type that implements core::iter::Collection:

for item in items {
process(item)
}

A loop does not consume the collection. Its pattern binds each element the way a let binds a value:

PatternBinds
for x in xsA copy of each element (the element type must be Copy)
for ref x in xsA read access to each element, read in place
for var x in xsA mutable copy of each element
for mut x in xsA mutable access to each element; writes change the collection

Use ref to loop over elements that are not Copy, and mut to update elements in place:

pub struct Account { pub balance: u256 }
#[test]
fn loop_binding_modes() {
var accounts = [Account { balance: 10 }, Account { balance: 20 }]
var total = 0
for ref account in accounts {
total += account.balance
}
assert!(total == 30)
for mut account in accounts {
account.balance *= 2
}
assert!(accounts[1].balance == 40)
}

Collections provide adapters that change how a loop walks them: enumerate(), reversed(), take(n), skip(n), step_by(k), filter(p) and map(f). They take closures and combine as method chains:

#[test]
fn loop_adapters() {
let xs: [u256; 5] = [1, 2, 3, 4, 5]
var weighted = 0
for (i, x) in xs.enumerate() {
weighted += i as u256 * x
}
assert!(weighted == 40)
var evens = 0
for x in xs.filter(|x| x % 2 == 0) {
evens += x
}
assert!(evens == 6)
var last_two = 0
for x in xs.reversed().take(2) {
last_two += x
}
assert!(last_two == 9)
// fold, any, count and find work without a loop
assert!(xs.fold(0, |acc, x| acc + x) == 15)
assert!(xs.any(|x| x > 4))
assert!(xs.count(|x| x % 2 == 1) == 3)
}

Destructure elements while iterating:

pub struct User { pub name: u256, pub balance: u256 }
for (index, value) in indexed_items {
store_at(index, value)
}
for ref user in users {
if user.balance > 0 {
credit(name: user.name, balance: user.balance)
}
}

Use while for condition-based loops:

var count: u256 = 5
while count > 0 {
process_next()
count = count - 1
}

A common pattern for indefinite loops:

while true {
if should_stop() {
break
}
do_work()
}

Use break to exit a loop early:

pub struct Item {
pub val: u256,
}
impl Copy for Item {}
for item in items {
if item.is_target() {
found = item
break // Exit the loop
}
}

Use continue to skip to the next iteration:

pub struct Item {
pub val: u256,
}
impl Copy for Item {}
for item in items {
if item.should_skip() {
continue // Skip this item
}
process(item)
}
var sum: u256 = 0
for value in values {
// Skip negative values
if value < 0 {
continue
}
// Stop if we exceed the limit
if sum + value > limit {
break
}
sum = sum + value
}

Use return to exit a function early:

fn find_user(id: u256) -> Option<User> {
if id == 0 {
return Option::None // Early return for invalid ID
}
// ... lookup logic ...
Option::Some(user) // Implicit return
}

Return a specific value from anywhere in the function:

fn calculate_discount(amount: u256, is_member: bool) -> u256 {
if amount < 100 {
return 0 // No discount for small amounts
}
if is_member {
return amount / 10 // 10% for members
}
amount / 20 // 5% for non-members (implicit return)
}

Fe functions return the value of their last expression implicitly. Use explicit return for:

  • Early exits from the function
  • Multiple exit points
  • Clarity in complex functions
// Implicit return - last expression is the return value
fn double(x: u256) -> u256 {
x * 2
}
// Explicit return - needed for early exit
fn safe_divide(a: u256, b: u256) -> Option<u256> {
if b == 0 {
return Option::None
}
Option::Some(a / b)
}
ConstructSyntaxDescription
ifif cond { }Conditional execution
if-elseif cond { } else { }Two-way branch
else ifif c1 { } else if c2 { }Multi-way branch
matchmatch val { pat => expr }Pattern matching
forfor pat in iter { }Iteration over collection
whilewhile cond { }Condition-based loop
breakbreakExit loop early
continuecontinueSkip to next iteration
returnreturn or return exprExit function early