Nesta página
Error Handling
Zolo has multiple layers of error handling: the Result type for recoverable errors, the ? propagation operator, and try/catch/finally for exception-style handling.
Result Type (Recap)¶
The idiomatic way to handle errors is with Result<T, E>:
use std::Result
fn divide(a: int, b: int) -> Result<int, str> {
if b == 0 {
return Result.Err("division by zero")
}
return Result.Ok(a / b)
}
match divide(10, 2) {
Result.Ok(v) => print("Result: {v}"),
Result.Err(e) => print("Error: {e}"),
}The ? Operator¶
The ? operator propagates errors automatically. If the value is Err, the function returns early with that error; if it is Ok, it unwraps the value:
use std::Result
fn parse_int(s: str) -> Result<int, str> {
// ... returns Ok or Err
}
fn compute(a: str, b: str) -> Result<int, str> {
let x = parse_int(a)? // returns Err early if parse fails
let y = parse_int(b)?
return Result.Ok(x + y)
}
print(compute("10", "20")) // Result.Ok(30)
print(compute("10", "oops")) // Result.Err("invalid integer: oops")Try / Catch / Finally¶
For exception-style error handling, Zolo provides try/catch/finally:
try {
let data = read_file("config.json")
process(data)
} catch e {
print("Error: {e}")
}With finally¶
The finally block always runs, whether or not an error occurred:
let file = open("data.txt")
try {
let content = file.read()
process(content)
} catch e {
print("Read error: {e}")
} finally {
file.close() // always executed
}Try as Expression¶
try/catch can be used as an expression that returns a value:
let result = try {
parse_json(raw_input)
} catch _ {
default_config()
}Nested Try/Catch¶
try {
let user = try {
fetch_user(id)
} catch _ {
create_guest_user()
}
let data = fetch_data(user.id)
render(data)
} catch e {
show_error_page(e)
}Panic¶
panic terminates the program with an error message. Use it for unrecoverable situations:
fn get_index(arr: [int], i: int) -> int {
if i < 0 or i >= arr.len() {
panic("index {i} out of bounds for array of length {arr.len()}")
}
return arr[i]
}Guard Clauses¶
guard is an early-return idiom for preconditions (similar to Swift's guard):
fn process(input: str?) {
guard input != nil else {
print("no input provided")
return
}
let value = input ?? ""
print("processing: {value}")
}
process("report")
process(nil)The guard keyword checks a condition. Its else branch must transfer
control with return, break, continue, panic, or another diverging
expression. After checking an optional for nil, use ?? to obtain the
non-optional value.
Option Handling¶
Option<T> represents a value that may or may not be present:
fn find_user(name: str) -> Option<User> {
// ...
}
// Unwrap with fallback
let user = find_user("Alice").unwrap_or(default_user())
// Map if present
let greeting = find_user("Alice").map(|u| "Hello, {u.name}!")
// Pattern match
match find_user("Bob") {
Some(u) => print("Found: {u.name}"),
None => print("Not found"),
}if let for Optional Unwrap¶
if let Some(user) = find_user("Alice") {
print("Found: {user.name}")
} else {
print("Not found")
}while let for Optional Iteration¶
while let Some(item) = queue.pop() {
process(item)
}Error Propagation Patterns¶
Chained Operations with ?¶
use std::Result
fn pipeline(path: str) -> Result<Report, str> {
let raw = read_file(path)?
let json = parse_json(raw)?
let data = validate(json)?
let report = generate_report(data)?
return Result.Ok(report)
}Converting Between Result and Option¶
// Option → Result
let r: Result<int, str> = Option.ok_or(find_value(), "not found")
// Result → Option (discards the error)
let o: Option<int> = divide(10, 2).ok()Summary¶
| Mechanism | Use Case |
|---|---|
Result<T, E> |
Recoverable errors, explicit error types |
? operator |
Propagate errors up the call stack |
try/catch/finally |
Exception-style handling with cleanup |
panic |
Unrecoverable errors, assertion failures |
guard |
Precondition checks with early return |
Option<T> + if let |
Optional values, safe unwrapping |