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.
