Use this guide when a value may be absent or an operation may fail. The primary question is not “which operator is shortest?” but “can the caller continue execution?”
Choose by meaning
| Situation | Use |
|---|---|
| No value is an ordinary outcome inside Topaz | Option<T> with Some(value) or None |
| External data explicitly carries a nullable field | T | null |
| The caller can respond to a meaningful error | Result<T, E> with Ok(value) or Err(error) |
| The program violated a runtime contract | A fault; evaluation aborts instead of producing a value |
Prefer Option for standard absence in Topaz APIs. Reserve null for data and
interop boundaries that explicitly contain it. Use Result when a failure
requires diagnostic details or a recovery decision.
Turn bad input into a recoverable result
This runnable example converts the absence returned by toInt into a
descriptive Result:
record StudyTask {
title: string,
minutes: int,
}
function parseMinutes(text: string) -> Result<int, string> {
match toInt(text) {
case Some(value) if value > 0 => Ok(value)
case _ => Err("minutes must be a positive integer")
}
}
function makeTask(title: string, minuteText: string) -> Result<StudyTask, string> {
let minutes = parseMinutes(minuteText)?
Ok(StudyTask { title: title, minutes: minutes })
}
function inspect(minuteText: string) -> () {
print("input: {minuteText}")
defer { print("inspection finished") }
match makeTask("Build application", minuteText) {
case Ok(task) => print("ready: {task.title}, {task.minutes} minutes")
case Err(message) => print("cannot add task: {message}")
}
}
inspect("25")
inspect("later")Check and run the program:
topaz check failures-resources.tpz
topaz run failures-resources.tpzinput: 25
ready: Build application, 25 minutes
inspection finished
input: later
cannot add task: minutes must be a positive integer
inspection finishedtoInt returns Option<int> because non-integer text yields no parsed value.
parseMinutes determines that this absence is significant to its caller and
converts it into Err. In makeTask, the postfix ? operator unwraps Ok.
Upon encountering Err, it propagates that error immediately from the returning
Result function. The final match expression handles both potential outcomes.
Work with optional and nullable values
Optional access and fallback operators maintain this distinction:
- For
maybeUser: Option<{ name: string }>,maybeUser?.nameevaluates to anOptionresult, andmaybeUser?.name ?? "guest"evaluates to astring. - For
configPath: string | null,configPath ?? "default.toml"evaluates the fallback only when the value isnull. target ??= valueis a statement that performs assignment only when the target is empty. AnOption<int>target requiresSome(42)rather than a bare42. Topaz does not wrap values inSomeautomatically.
Both ?. and ?? evaluate their left operand once. ?. also skips evaluating
method arguments when the receiver is empty. Each operator unwraps or flattens a single
optional layer without converting Option into null.
Common correction: Use standard . property access for non-optional and non-nullable
values. If a type combines Option<T> and null, handle cases explicitly with pattern
matching rather than chaining optional operators.
A fault is not an error value
This example requests an out-of-bounds array element:
let tasks = ["Run first program", "Build application"]
print(tasks[2])Running topaz run intentional-fault.tpz exits with a non-zero status:
error[TPZ4001]: index 2 is out of bounds for an array of length 2
--> intentional-fault.tpz:2:7
|
2 | print(tasks[2])
| ^^^^^^^^
The bounds violation constitutes a fault. It halts evaluation immediately and is neither
None nor Err. Postfix ?, ??, optional chaining, and concurrent cannot catch
or convert faults. Constant expressions containing invalid arithmetic may instead be
rejected during static checking prior to execution.
Exact boundaries
- Postfix
?is valid only within functions or closures whose return type can represent the corresponding error value. - Traditional
tryblocks, exception handling, fault catching, panic keywords, and automatic retry mechanisms are not supported in Topaz. assert(...)is available only in explicitly selectedtest-profilebuilds. It is not an assertion mechanism for general application logic.- Resource cleanup on normal scope exit should use
defer. Fault cleanup operates within a narrower runtime scope, so faults must not be treated as recoverableResultvalues.
Continue with Failures and Resources for the guided exercise, Defer & Resources for cleanup execution timing, and Runtime Behavior for the runtime fault boundary model.