Error Handling
Mux uses explicit error handling through the result<T, E> and optional<T> types, avoiding exceptions entirely.
result<T, E>
The result type represents operations that can succeed with a value of type T or fail with an error of type E.
E must implement the Error interface:
string implements Error, so existing result<T, string> code continues to work.
Basic Usage
result Variants
Creating result Values
result Methods
| Method | Returns | Description |
|---|---|---|
.is_ok() | bool | Returns true if the result is an ok variant |
.is_err() | bool | Returns true if the result is an err variant |
.to_string() | string | String representation |
Pattern Matching Results
Ignoring Error Details
Use _ when you don't need the error value:
optional<T>
The optional type represents values that may or may not exist.
Basic Usage
optional Methods
| Method | Returns | Description |
|---|---|---|
.is_some() | bool | Returns true if the optional contains a value |
.is_none() | bool | Returns true if the optional is empty |
.to_string() | string | String representation |
result Methods
Creating optional Values
Safe Collection Access
Collections return optional<T> for safe access:
Map Lookups
Ignoring the Value
Use _ when you only care about presence/absence:
Combining result and optional
optional of result
result of optional
Error Propagation Patterns
Early Returns
Nested Matching
Fallible Type Conversions
String and char parsing return result because they can fail:
Technical Implementation
Memory Layout
Both types use a uniform runtime representation:
pub struct result<T, E> {
discriminant: i32, // 0 = ok, 1 = err
data: *mut T, // pointer to value
}
pub struct optional<T> {
discriminant: i32, // 0 = none, 1 = some
data: *mut T, // pointer to value
}Benefits:
- Single runtime representation: Collections can store either
- No enum overhead: No runtime enum tag beyond discriminant
- Easy error propagation: Simple with match statements
- Interop: optional and result can wrap the same types
Runtime ABI note
• Implementation detail: the runtime now represents both optional<T> and result<T, E> as boxed Value pointers (*mut Value) at the FFI boundary. This means runtime constructors and C-exported helpers return *mut Value for these types. Compiler-generated code and native extensions should treat optionals/results as boxed Value objects and use the provided discriminant helpers when matching variants.
Comparison with Other Languages
vs Rust
Very similar:
// Rust
fn divide(a: i32, b: i32) -> result<i32, String> {
if b == 0 {
err("division by zero".to_string())
} else {
ok(a / b)
}
}
// Mux
func divide(int a, int b) returns result<int, string> {
if b == 0 {
return err("division by zero")
}
return ok(a / b)
}Differences:
- Mux uses explicit
returnstatements - Rust has
?operator for error propagation (Mux doesn't)
vs Go
Similar philosophy, different syntax:
// Go
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
// Mux
func divide(int a, int b) returns result<int, string> {
if b == 0 {
return err("division by zero")
}
return ok(a / b)
}Mux advantage:
- Type system enforces error handling
- Cannot ignore errors without explicit match
vs Exceptions (Java/Python)
Different paradigm:
# Python
def divide(a, b):
if b == 0:
raise ValueError("division by zero")
return a / b
# Mux
func divide(int a, int b) returns result<int, string> {
if b == 0 {
return err("division by zero")
}
return ok(a / b)
}Mux advantages:
- Errors visible in function signature
- Cannot forget to handle errors
- No runtime exceptions
- No try/catch blocks
Best Practices
- Return result for fallible operations - Parse failures, I/O operations, validation
- Return optional for nullable values - Collection access, lookups, searches
- Match exhaustively - Handle both success and error cases
- Use descriptive error messages - Include context in error strings
- Early returns for errors - Reduces nesting
- Use
_for ignored values - Makes intent explicit - Don't overuse wildcards - Match specific cases when possible
- Document error conditions - What errors can a function return?
- Chain operations explicitly - No
?operator, use match - Prefer result over panicking - Explicit > implicit
Common Patterns
Validation
Lookup with Default
Transform result
See Also
- Enums - result and optional as tagged unions
- Control Flow - Pattern matching with match
- Types - Fallible type conversions
- Collections - Safe collection access with optional