Mojo Optional Types

An Optional value either holds a real value or holds nothing. It is a clean way to represent "this result may be absent" without using null pointers, magic sentinel values like -1, or exceptions. Mojo's type system forces you to check whether the value exists before you use it, preventing a common class of bugs.

The Gift Box Analogy

  Optional[Int]

  Case 1: Box contains a value    Case 2: Box is empty
  ┌────────────────┐              ┌────────────────┐
  │  Optional[Int] │              │  Optional[Int] │
  │  ┌──────────┐  │              │                │
  │  │    42    │  │              │    (nothing)   │
  │  └──────────┘  │              │                │
  └────────────────┘              └────────────────┘
       has_value() = True              has_value() = False

  You must open the box and check before taking the value out.
  Reaching into an empty box = runtime error.

Creating an Optional

from collections import Optional

fn main():
    var with_value = Optional[Int](42)
    var empty = Optional[Int](None)

    print(with_value.has_value())   # True
    print(empty.has_value())        # False

Checking and Unwrapping

Always check has_value() before calling value(). Calling value() on an empty Optional causes a runtime error.

from collections import Optional

fn main():
    var result = Optional[Float64](3.14)

    if result.has_value():
        print("Got:", result.value())   # Got: 3.14
    else:
        print("No value present")

    var missing = Optional[Float64](None)
    if not missing.has_value():
        print("Missing value handled safely")

Returning Optional from Functions

Functions that might not produce a result return Optional instead of raising an error. This communicates to callers that absence is a normal, expected outcome — not an error.

from collections import Optional

fn find_first_positive(numbers: List[Int]) -> Optional[Int]:
    for i in range(len(numbers)):
        if numbers[i] > 0:
            return Optional[Int](numbers[i])
    return Optional[Int](None)

fn main():
    var nums1 = List[Int](-3, -1, 5, 2)
    var nums2 = List[Int](-5, -2, -8)

    var r1 = find_first_positive(nums1)
    if r1.has_value():
        print("First positive:", r1.value())   # First positive: 5

    var r2 = find_first_positive(nums2)
    if r2.has_value():
        print("Found:", r2.value())
    else:
        print("No positive number found")      # No positive number found
Function flow:
  nums1: [-3, -1, 5, 2]
    i=0: -3 > 0? No
    i=1: -1 > 0? No
    i=2:  5 > 0? Yes → return Optional(5)

  nums2: [-5, -2, -8]
    i=0,1,2: all <= 0
    loop ends → return Optional(None)

value_or() — Safe Default

The value_or(default) method returns the contained value if present, or the provided default if absent. This is the cleanest way to handle the two cases in one line.

from collections import Optional

fn get_config_timeout() -> Optional[Int]:
    return Optional[Int](None)   # Not set in this example

fn main():
    var timeout = get_config_timeout()
    var actual_timeout = timeout.value_or(30)   # 30 is the fallback
    print("Timeout:", actual_timeout)           # Timeout: 30

    var explicit = Optional[Int](60)
    print(explicit.value_or(30))   # 60 — uses the stored value
value_or() logic:
  if has_value():
      return stored_value
  else:
      return default_argument

Optional in Struct Fields

from collections import Optional

struct UserProfile:
    var username: String
    var bio: Optional[String]
    var age: Optional[Int]

    fn __init__(inout self, username: String):
        self.username = username
        self.bio = Optional[String](None)
        self.age = Optional[Int](None)

    fn set_bio(inout self, text: String):
        self.bio = Optional[String](text)

    fn set_age(inout self, a: Int):
        self.age = Optional[Int](a)

    fn display(self):
        print("User:", self.username)
        print("Bio:", self.bio.value_or("(not set)"))
        if self.age.has_value():
            print("Age:", self.age.value())
        else:
            print("Age: (private)")

fn main():
    var user = UserProfile("mojo_dev")
    user.set_bio("I build fast software.")
    user.display()

Output:

User: mojo_dev
Bio: I build fast software.
Age: (private)

Optional vs Error Handling

Use Optional when:                Use raises/Error when:
  ✓ Absence is normal             ✓ Something went wrong unexpectedly
  ✓ "item may not exist"          ✓ Invalid input that should not happen
  ✓ Dictionary key lookup         ✓ Network failure, disk error
  ✓ Finding something in a list   ✓ Business rule violation

Example:
  Dict lookup → Optional          Divide by zero → Error
  "User may not exist" → Optional "Corrupt data file" → Error

Key Takeaways

Optional[T] represents a value that may or may not exist. Create with Optional[T](value) or Optional[T](None). Always call has_value() before value(). Use value_or(default) for a compact one-line fallback. Return Optional from functions when absence is an expected outcome rather than an error. Store optional fields in structs to model real-world data where some properties may be unknown or unset.

Leave a Comment

Your email address will not be published. Required fields are marked *