Part 0 · 2 chapters · ~12 min
Basics
Installing with rustup, cargo projects, variables and mutability, scalar and compound types, functions and expressions, control flow and match, structs and enums, Option and Result with the ? operator, modules and crates, and the standard library tour.
1
A first program
code
rustup default stable && cargo new ledger && cd ledger && cargo run
// src/main.rs (compiled and run on this machine: fee(5_000_000) = Ok(25000))
fn fee(amount_kobo: i64) -> Result<i64, String> {
if amount_kobo <= 0 { return Err(format!("amount must be positive, got {amount_kobo}")); }
Ok((amount_kobo * 50 / 10_000).max(1_000)) // the last expression is the return value
}
fn main() {
let amounts = vec![100_000, 5_000_000]; // immutable binding by default
let mut total = 0; // `mut` to allow change
for a in &amounts {
match fee(*a) {
Ok(f) => { total += f; println!("{a} → {f}"); }
Err(e) => eprintln!("error: {e}"),
}
}
println!("total fees: {total}");
}2
Enums, Option, Result and ?
code
enum TransferState { Pending { since: u64 }, Completed { id: String }, Failed { reason: String } } // data in each variant
fn label(s: &TransferState) -> String {
match s { // exhaustive: missing a variant is a compile error
TransferState::Pending { since } => format!("pending since {since}"),
TransferState::Completed { id } => format!("sent ({id})"),
TransferState::Failed { reason } => format!("failed: {reason}"),
}
}
// no null: absence is Option<T>; failure is Result<T, E>; `?` returns early on Err or None
fn total_fee(raw: &str) -> Result<i64, Box<dyn std::error::Error>> {
let amount: i64 = raw.trim().parse()?; // ParseIntError converts into the boxed error
Ok(fee(amount)?)
}Rust has no null and no exceptions: absence is Option, failure is Result, and the compiler makes you handle both. That is a large part of why Rust programs that compile tend to work.