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

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, or any other type that implements core::seq::Seq:

for item in items {
process(item)
}

Arrays implement Seq only when their element type is Copy, because each iteration copies an element out of the array. Primitive element types such as u256 work out of the box. To loop over an array of your own structs, implement Copy for the struct (impl Copy for User {}); otherwise the compiler reports a move conflict.

Destructure elements while iterating:

pub struct User { pub name: u256, pub balance: u256 }
impl Copy for User {} // required to iterate over [User; N]
for (index, value) in indexed_items {
store_at(index, value)
}
for User { name, balance } in users {
if balance > 0 {
credit(name, balance)
}
}

Use while for condition-based loops:

let mut 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)
}
let mut 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