Skip to main content

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:

error_interface.mux
Loading...

string implements Error, so existing result<T, string> code continues to work.

Basic Usage

result_basic.mux
Loading...

result Variants

result<T, E> is built in, not a user enum. It has two cases - ok carrying a T, and err carrying an E - and you construct them with the ok and err functions rather than through an enum name. Declaring your own type named result is an error, because it used to overwrite the built-in silently.

The error type must implement Error, which means supplying message(). string does, so result<int, string> works out of the box; a class can too:

custom_error_type.mux
Loading...

Creating result Values

creating_results.mux
Loading...

result Methods

MethodReturnsDescription
.is_ok()boolReturns true if the result is an ok variant
.is_err()boolReturns true if the result is an err variant
.to_string()stringString representation
result_methods.mux
Loading...

Pattern Matching Results

pattern_matching_results.mux
Loading...

Ignoring Error Details

Use _ when you don't need the error value:

ignoring_error_details.mux
Loading...

optional<T>

The optional type represents values that may or may not exist.

Basic Usage

optional_basic.mux
Loading...

optional Methods

MethodReturnsDescription
.is_some()boolReturns true if the optional contains a value
.is_none()boolReturns true if the optional is empty
.to_string()stringString representation
optional_methods.mux
Loading...

result Methods

optional<T> is built in and has two cases - some carrying a T, and none. Like result, you build it with the some and none functions rather than through an enum name, and you cannot declare a type named optional yourself:

creating_optional_values.mux
Loading...

Creating optional Values

creating_optionals.mux
Loading...

Safe Collection Access

Collections return optional<T> for safe access:

safe_collection_access.mux
Loading...

Map Lookups

map_lookups.mux
Loading...

Ignoring the Value

Use _ when you only care about presence/absence:

ignoring_values.mux
Loading...

Combining result and optional

optional of result

optional_of_result.mux
Loading...

result of optional

result_of_optional.mux
Loading...

Error Propagation Patterns

Early Returns

early_returns.mux
Loading...

Nested Matching

nested_matching.mux
Loading...

Fallible Type Conversions

String and char parsing return result because they can fail:

fallible_conversions.mux
Loading...

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 return statements
  • 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

  1. Return result for fallible operations - Parse failures, I/O operations, validation
  2. Return optional for nullable values - Collection access, lookups, searches
  3. Match exhaustively - Handle both success and error cases
  4. Use descriptive error messages - Include context in error strings
  5. Early returns for errors - Reduces nesting
  6. Use _ for ignored values - Makes intent explicit
  7. Don't overuse wildcards - Match specific cases when possible
  8. Document error conditions - What errors can a function return?
  9. Chain operations explicitly - No ? operator, use match
  10. Prefer result over panicking - Explicit > implicit

Common Patterns

Validation

validation_pattern.mux
Loading...

Lookup with Default

lookup_default.mux
Loading...

Transform result

transform_result.mux
Loading...

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