Option<T> β Surviving in a World Without nil
How Ruby's nil and &. operator translate to Rust
In Ruby, nil is everywhere. Access a missing array index β nil. Missing hash key β nil. Method returns nothing explicitly β nil. Call a method on nil β NoMethodError. A huge portion of production errors come from here.
Rust eliminated nil entirely. Instead, it uses the Option<T> enum.
enum Option<T> {
Some(T), // value exists
None, // no value
}
Compared to Ruby
# Ruby
def find_user(id)
users[id] # might be nil
end
user = find_user(99)
user.name # NoMethodError if nil!
// Rust
fn find_user(id: u32) -> Option<User> {
// Some(user) or None
}
let user = find_user(99);
// user.name // compile error! Option<User> has no name method
Rust treats Option<User> and User as different types. You must extract the value from Option to use it.
Extracting β unwrap (dangerous)
let user = find_user(99).unwrap(); // panics if None!
Similar risk to calling methods on nil in Ruby. Avoid in production code.
Safe extraction β match, if let, map
// match
match find_user(99) {
Some(user) => println!("{}", user.name),
None => println!("user not found"),
}
// if let (check one case)
if let Some(user) = find_user(99) {
println!("{}", user.name);
}
// map β closest to Ruby's &.
let name = find_user(99).map(|u| u.name);
// returns Option<String>
&. β map/and_then mapping
Ruby's safe navigation vs Option methods:
# Ruby
user&.name # returns nil if nil
user&.address&.city # chaining
user&.name || "unknown" # default
// Rust
user.map(|u| u.name) // None if None
user.and_then(|u| u.address).map(|a| a.city) // chaining
user.map(|u| u.name).unwrap_or("unknown") // default
map transforms the value inside Some. and_then is for when the transform returns another Option (flatMap). unwrap_or provides a default for None.
unwrap_or_else β when the default is expensive
let name = find_user(99)
.map(|u| u.name)
.unwrap_or_else(|| fetch_default_name()); // only runs on None
Ruby's || in user&.name || expensive_default doesn't evaluate the right side unless needed. Rust's unwrap_or_else has the same lazy evaluation.
Key Points
Option<T> is Some(value) or None β type-safe nil replacement
unwrap is dangerous β use match, if let, map for safe handling
&. β map, chaining β and_then, default β unwrap_or
Option<User> and User are different types β must extract to use
Pros
- ✓ NoMethodError on nil is literally impossible β caught at compile time
- ✓ Forgetting to handle None means code won't compile
Cons
- ✗ Can't ignore nil like Ruby β feels cumbersome early on
- ✗ Long Option chains can reduce readability