• Option
• Result
• The ? operator propagates errors up the call stack concisely
• Combinators like .map(), .and_then(), .unwrap_or() chain fallible operations
• The compiler forces you to handle every error case — no silent failures
• Option
• Result
• The ? operator propagates errors up the call stack concisely
• Combinators like .map(), .and_then(), .unwrap_or() chain fallible operations
• The compiler forces you to handle every error case — no silent failures
cargo test
// 005: Reverse a List
// Approach 1: Built-in (in-place)
fn reverse_inplace(v: &mut Vec<i32>) {
v.reverse();
}
// Approach 2: Iterator-based (new Vec)
fn reverse_iter(v: &[i32]) -> Vec<i32> {
v.iter().rev().copied().collect()
}
// Approach 3: Fold-based (accumulator pattern)
fn reverse_fold(v: &[i32]) -> Vec<i32> {
v.iter().fold(vec![], |mut acc, &x| {
acc.insert(0, x);
acc
})
}
// Approach 3b: Recursive
fn reverse_recursive(v: &[i32]) -> Vec<i32> {
if v.is_empty() {
vec![]
} else {
let mut rest = reverse_recursive(&v[1..]);
rest.push(v[0]);
rest
}
}
fn main() {
let v = vec![1, 2, 3, 4, 5];
println!("reverse_iter = {:?}", reverse_iter(&v));
println!("reverse_fold = {:?}", reverse_fold(&v));
let mut m = v.clone();
reverse_inplace(&mut m);
println!("reverse_inplace = {:?}", m);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_reverse_inplace() {
let mut v = vec![1, 2, 3, 4, 5];
reverse_inplace(&mut v);
assert_eq!(v, vec![5, 4, 3, 2, 1]);
}
#[test]
fn test_reverse_iter() {
assert_eq!(reverse_iter(&[1, 2, 3, 4, 5]), vec![5, 4, 3, 2, 1]);
assert_eq!(reverse_iter(&[]), Vec::<i32>::new());
}
#[test]
fn test_reverse_fold() {
assert_eq!(reverse_fold(&[1, 2, 3]), vec![3, 2, 1]);
}
#[test]
fn test_reverse_recursive() {
assert_eq!(reverse_recursive(&[1, 2, 3, 4, 5]), vec![5, 4, 3, 2, 1]);
assert_eq!(reverse_recursive(&[42]), vec![42]);
assert_eq!(reverse_recursive(&[]), Vec::<i32>::new());
}
}
(* 005: Reverse a List *)
(* Approach 1: Built-in *)
let reverse_builtin lst = List.rev lst
(* Approach 2: Naive recursive — O(n^2) *)
let rec reverse_naive = function
| [] -> []
| x :: xs -> reverse_naive xs @ [x]
(* Approach 3: Tail-recursive with accumulator — O(n) *)
let reverse_acc lst =
let rec aux acc = function
| [] -> acc
| x :: xs -> aux (x :: acc) xs
in
aux [] lst
(* Tests *)
let () =
assert (reverse_builtin [1; 2; 3; 4; 5] = [5; 4; 3; 2; 1]);
assert (reverse_builtin [] = []);
assert (reverse_naive [1; 2; 3] = [3; 2; 1]);
assert (reverse_acc [1; 2; 3; 4; 5] = [5; 4; 3; 2; 1]);
assert (reverse_acc [] = []);
assert (reverse_acc [42] = [42]);
Printf.printf "✓ All tests passed\n"
Reversing a list reveals the importance of accumulator patterns. The naive approach (reverse tail, append head) creates O(n²) work. The tail-recursive approach (prepend to accumulator) achieves O(n).
| Approach | OCaml | Rust |
|---|---|---|
| Built-in | `List.rev` | `.reverse()` / `.rev()` |
| Naive recursive | `rev tail @ [head]` | `rec_reverse(&v[1..]).push(v[0])` |
| Tail-recursive | `aux (x::acc) xs` | loop with accumulator |
| Complexity (good) | O(n) | O(n) |