Skip to main content

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();
warning

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

MethodRust EquivalentDescription
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

OxideRustDescription
opt ?? defaultopt.unwrap_or(default)Null coalescing
opt!!opt.unwrap()Force unwrap
opt?.method()opt.as_ref().map(|v| v.method())Safe access
opt != nullopt.is_some()Check if Some
opt == nullopt.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