Option Methods
Options in Oxide use Rust's standard Option<T> methods directly, with additional operators for cleaner syntax.
Oxide Operators
Oxide provides three operators for working with optionals that make code more concise:
Null Coalescing (??)
Provides a default value when the optional is null. Equivalent to Rust's unwrap_or().
let name: String? = null
let result = name ?? "Default" // "Default"
let value: Int? = 42
let result = value ?? 0 // 42
// With expressions
let config = loadConfig() ?? Config.default()
Rust equivalent:
let result = name.unwrap_or("Default".to_string());
let result = value.unwrap_or(0);
Force Unwrap (!!)
Unwraps the value, panicking if null. Equivalent to Rust's unwrap().
let name: String? = "Alice"
let unwrapped: String = name!! // "Alice"
// Panics if null!
let missing: String? = null
let crash = missing!! // PANIC!
Rust equivalent:
let unwrapped = name.unwrap();
Only use !! when you're certain the value is not null. Prefer ?? or pattern matching for safer code.
Safe Access (?.)
Calls a method on an optional, returning null if the value is null. Equivalent to Rust's opt.as_ref().map(|v| v.method()).
let name: String? = "hello"
// Call methods on optional
let length: Int? = name?.len() // Some(5)
let upper: String? = name?.toUppercase() // Some("HELLO")
// Chain safe calls
let user: User? = getUser()
let city: String? = user?.address?.city // Chains through nested optionals
// Combined with null coalescing
let length = name?.len() ?? 0 // 5 (or 0 if name is null)
Rust equivalent:
let length = name.as_ref().map(|n| n.len());
let upper = name.as_ref().map(|n| n.to_uppercase());
let city = user.as_ref().and_then(|u| u.address.as_ref()).and_then(|a| a.city.clone());
Idiomatic Null Checks
Use != null and == null for null checks:
let opt: Int? = 42
// Idiomatic (preferred)
if opt != null {
println!("Has value")
}
// Also valid
if opt == null {
println!("No value")
}
Rust equivalent:
if opt.is_some() { ... }
if opt.is_none() { ... }
Methods
| Method | Rust Equivalent | Description |
|---|---|---|
unwrapOr(default) | unwrap_or(default) | Returns value or default |
unwrapOrElse(f) | unwrap_or_else(f) | Returns value or computes default |
map(f) | map(f) | Transforms the value |
andThen(f) | and_then(f) | Transforms and flattens |
filter(predicate) | filter(predicate) | Filters by predicate |
isSome() | is_some() | Returns true if Some |
isNone() | is_none() | Returns true if None |
ok_or(err) | ok_or(err) | Converts to Result |
Examples
Unwrap with Default
let some: Int? = 42
let none: Int? = null
// Using ?? operator (preferred)
some ?? 0 // 42
none ?? 0 // 0
// Using method
some.unwrapOr(0) // 42
none.unwrapOr(0) // 0
// Computed default (lazy)
none.unwrapOrElse { computeExpensiveDefault() }
Mapping Values
let opt: Int? = 42
// Transform the value if present
let doubled = opt.map { it * 2 } // Some(84)
let asString = opt.map { "Value: $it" } // Some("Value: 42")
// Chaining maps
let result = opt
.map { it * 2 }
.map { it + 10 } // Some(94)
Flat Mapping with andThen
fn findUser(id: Int): User? { ... }
fn getAddress(user: User): Address? { ... }
let userId: Int? = 42
// With andThen - flattened
let address: Address? = userId
.andThen { findUser(it) }
.andThen { getAddress(it) }
// Or with safe access
let address = findUser(userId!!)?.address
Filtering
let opt: Int? = 42
// Keep only if predicate passes
opt.filter { it > 10 } // Some(42)
opt.filter { it > 100 } // None
// Practical example
let age: Int? = 25
let adultAge = age.filter { it >= 18 }
Pattern Matching
If-Let
if let value = optionalValue {
println!("Got: $value")
}
Guard-Let
fn process(opt: Int?) {
guard let value = opt else {
return
}
// value is non-optional here
println!("Processing $value")
}
Match
match optionalValue {
null -> println!("No value")
Some(value) -> println!("Got: $value")
}
Complete Comparison with Rust
| Oxide | Rust | Description |
|---|---|---|
opt ?? default | opt.unwrap_or(default) | Null coalescing |
opt!! | opt.unwrap() | Force unwrap |
opt?.method() | opt.as_ref().map(|v| v.method()) | Safe access |
opt != null | opt.is_some() | Check if Some |
opt == null | opt.is_none() | Check if None |
opt.unwrapOr(d) | opt.unwrap_or(d) | Unwrap with default |
opt.unwrapOrElse(f) | opt.unwrap_or_else(f) | Unwrap with lazy default |
opt.map(f) | opt.map(f) | Map value |
opt.andThen(f) | opt.and_then(f) | Flat map |
opt.filter(p) | opt.filter(p) | Filter |
if let v = opt { } | if let Some(v) = opt { } | If-let binding |
guard let v = opt else { } | N/A (use if-let or match) | Guard-let |
Common Patterns
Optional Chaining
let user: User? = getUser()
let city = user?.address?.city ?? "Unknown"
Converting to Result
let opt: Int? = getValue()
let result = opt.ok_or("Value not found")
Iterating
let opt: Int? = 42
// Option implements IntoIterator
for value in opt {
println!(value) // Runs once if Some, not at all if None
}
Combining Options
let a: Int? = 1
let b: Int? = 2
// Both must be present
if let x = a, let y = b {
println!("Sum: ${x + y}")
}
Additional Rust Methods
Since Oxide uses Rust's Option methods directly, you can use any method from Rust's standard library:
let opt: Int? = 42
// Check methods
opt.isSome() // true
opt.isNone() // false
// Get inner reference
opt.asRef() // Option<&Int>
// Replace value
var mutable: Int? = 10
mutable.take() // returns Some(10), sets to None
mutable.replace(20) // returns None, sets to Some(20)
// Logical operations
let a: Int? = 1
let b: Int? = 2
a.or(b) // Some(1) - returns a if Some, else b
a.and(b) // Some(2) - returns b if both Some
a.xor(b) // None - returns Some if exactly one is Some
See Also
- Null Safety - Complete null safety guide
- Result Methods - Error handling
- Standard Library Overview