Result (std::Result)
std::Result represents either a success (Result.Ok(value)) or a
failure (Result.Err(message)). Functions that can fail return a Result
instead of throwing exceptions — the caller decides how to react.
Ok and Err
The two constructors and the predicates is_ok / is_err:
A divide function that returns Ok or Err depending on the divisor.
// Feature: Result.Ok / Result.Err — the two variants of the type
// When to use: representing success or failure without using exceptions.
use std::Result
let ok = Result.Ok(42)
let err = Result.Err("failed")
// is_ok / is_err — quick checks.
print(ok.is_ok()) // expected: true
print(ok.is_err()) // expected: false
print(err.is_ok()) // expected: false
print(err.is_err()) // expected: true
// Function that returns a Result.
fn divide(a: int, b: int) -> Result<int, str> {
if b == 0 {
return Result.Err("division by zero")
}
return Result.Ok(a / b)
}
let r1 = divide(10, 2)
let r2 = divide(10, 0)
print(r1.is_ok()) // expected: true
print(r2.is_err()) // expected: true
Unwrap
unwrap() extracts the value from an Ok — panics on Err. Use
unwrap_or(default) to provide a safe fallback:
unwrap_or never panics; unwrap_err extracts the error message.
// Feature: .unwrap / .unwrap_or / .unwrap_err — extracting the value
// When to use: pulling the inner value; unwrap_or avoids panic on Err.
use std::Result
let ok = Result.Ok(42)
let err = Result.Err("oops")
// unwrap — returns the value; PANICS on Err.
print(ok.unwrap()) // expected: 42
// unwrap_or — safe default when Err.
print(err.unwrap_or(0)) // expected: 0
print(ok.unwrap_or(999)) // expected: 42
// unwrap_err — returns the error; PANICS on Ok.
print(err.unwrap_err()) // expected: oops
// Safe pattern: test first.
fn show(r: Result<int, str>) {
if r.is_ok() {
print("ok: {r.unwrap()}")
} else {
print("err: {r.unwrap_err()}")
}
}
show(Result.Ok(10)) // expected: ok: 10
show(Result.Err("failure")) // expected: err: failure
Map
map transforms the inner value of an Ok without leaving the Result. An
Err passes through untouched. map_err does the same for the error side:
Chaining map for multiple transformations.
// Feature: Result.map / Result.map_err — transform within a Result
// When to use: applying a transformation to the value without unwrapping.
use std::Result
let ok = Result.Ok(10)
let err = Result.Err("failure")
// map — transforms the inner value when Ok; passes Err through unchanged.
let doubled = ok.map(|x| x * 2)
print(doubled.unwrap()) // expected: 20
let still_err = err.map(|x| x * 2)
print(still_err.is_err()) // expected: true
print(still_err.unwrap_err()) // expected: failure
// map_err — transforms the error when Err; passes Ok through unchanged.
let prefixed = err.map_err(|e| "ERROR: " + e)
print(prefixed.unwrap_err()) // expected: ERROR: failure
let ok2 = ok.map_err(|e| "never")
print(ok2.unwrap()) // expected: 10
// Chain map for multiple transformations.
let r = Result.Ok(5)
let r2 = r.map(|x| x + 1)
let r3 = r2.map(|x| x * 10)
print(r3.unwrap()) // expected: 60
And Then
and_then chains operations that themselves return a Result. The first Err
stops the chain and propagates:
Two steps that can fail; and_then eliminates manual checking.
// Feature: Result.and_then — chain operations that return a Result
// When to use: pipelines with multiple failing steps; each callback
// returns a fresh Result.
use std::Result
fn parse_int(s: str) -> Result<int, str> {
if s == "1" { return Result.Ok(1) }
if s == "2" { return Result.Ok(2) }
if s == "3" { return Result.Ok(3) }
return Result.Err("parse error: {s}")
}
fn double_if_even(n: int) -> Result<int, str> {
if n % 2 == 0 {
return Result.Ok(n * 2)
}
return Result.Err("not even: {n}")
}
// Happy path — all Ok.
let r1 = parse_int("2").and_then(double_if_even)
print(r1.is_ok()) // expected: true
print(r1.unwrap()) // expected: 4
// Failure in the 1st step — Err propagates.
let r2 = parse_int("z").and_then(double_if_even)
print(r2.is_err()) // expected: true
// Failure in the 2nd step.
let r3 = parse_int("1").and_then(double_if_even)
print(r3.is_err()) // expected: true
print(r3.unwrap_err()) // expected: not even: 1
The ? Operator
? after a Result expression extracts the Ok or immediately returns
the Err to the caller — equivalent to if is_err { return } without the
noise:
add with ? vs. add_v1 without ? — identical in behaviour, different in clarity.
// Feature: The `?` operator — propagates Err automatically
// When to use: clean code in pipelines; equivalent to if-let-Err-return.
use std::Result
fn parse_int(s: str) -> Result<int, str> {
if s == "10" { return Result.Ok(10) }
if s == "5" { return Result.Ok(5) }
return Result.Err("parse: {s}")
}
// Without `?`: every call needs to be checked manually.
fn add_v1(a: str, b: str) -> Result<int, str> {
let ra = parse_int(a)
if ra.is_err() { return ra }
let rb = parse_int(b)
if rb.is_err() { return rb }
return Result.Ok(ra.unwrap() + rb.unwrap())
}
// With `?`: each `?` extracts the Ok or returns the Err immediately.
fn add(a: str, b: str) -> Result<int, str> {
let x = parse_int(a)?
let y = parse_int(b)?
return Result.Ok(x + y)
}
// Happy path.
let r1 = add("10", "5")
print(r1.unwrap()) // expected: 15
// Failure — first Err propagates.
let r2 = add("oops", "5")
print(r2.is_err()) // expected: true
print(r2.unwrap_err()) // expected: parse: oops
// Failure on the second call.
let r3 = add("10", "xyz")
print(r3.unwrap_err()) // expected: parse: xyz
// Both versions are equivalent.
print(add_v1("10", "5").unwrap()) // expected: 15
Challenge
Write a function sqrt_positive(n: int) -> Result<float, str> that returns
Err when n < 0 and Ok(sqrt(n)) otherwise. Then compose it with
and_then to compute the sum of two square roots.
See also