Learn Topaz

Failures and Resources

Reject invalid study durations with Option and Result, follow cleanup with defer, and correct an array access fault.

Handle invalid study durations without stopping the program, and watch a deferred action run after each inspection. Then trigger an array access error and fix the index that caused it.

Complete Data and Control first. Use the same installation and working folder.

Handle valid and invalid input

Save this code as failures-resources.tpz.

TOPAZ
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")

In that folder, check the file and run it.

BASH
topaz check failures-resources.tpz
topaz run failures-resources.tpz

Both commands finish with exit code 0. The program reports the invalid input and continues normally.

Output
input: 25
ready: Build application, 25 minutes
inspection finished
input: later
cannot add task: minutes must be a positive integer
inspection finished

Follow the input through Option and Result

Option represents a value that may be absent. toInt("25") returns Some(25), while toInt("later") returns None because it cannot parse an integer.

Result holds either a success value in Ok or an error value in Err. The function parseMinutes returns Ok(value) when the parsed integer passes value > 0. For missing, zero, or negative values, it returns Err with an explanation.

In makeTask, the ? after parseMinutes(minuteText) takes the integer out of Ok. If it receives Err, it returns that error from makeTask immediately. The task is then never constructed. This works because both functions return a compatible Result with a string error.

The function inspect matches the result and prints either the new task or the error message.

Run an action when the function finishes

defer registers an action to run when execution leaves the enclosing scope, here the body of inspect. That is why inspection finished follows each inspection. The error message for "later" is an ordinary result that the program handles.

Use defer for cleanup such as closing an opened file. Registered cleanup also runs when return or ? exits its scope. A runtime fault stops evaluation immediately, and cleanup after that point is unspecified. Handle expected input failures with Result so normal cleanup can run.

Find and fix an invalid array index

Save this separate program as intentional-fault.tpz.

TOPAZ
let tasks = ["Run first program", "Build application"]
print(tasks[2])

Run topaz run intentional-fault.tpz. It exits with a nonzero code and reports the invalid access.

Output
error[TPZ4001]: index 2 is out of bounds for an array of length 2
 --> intentional-fault.tpz:2:7
  |
2 | print(tasks[2])
  |       ^^^^^^^^

The two elements have indices 0 and 1. Index 2 requests an element that does not exist. This causes a runtime fault, which stops evaluation. It produces no Err value for match or ? to handle.

On line 2, change tasks[2] to tasks[1] and run the file again. It prints Build application and exits with code 0.

Choose how to handle a missing or invalid value

SituationWhat to use or do
A value may be absentOption with Some(value) or None
The caller needs a success value or an error explanationResult with Ok(value) or Err(error)
A function should pass an error to its caller? inside a function with a compatible return type
An invalid operation causes a runtime faultCorrect the operation or check its inputs before executing it
A scope owns cleanup workRegister that work with defer

Try this

Add inspect("0") at the end of failures-resources.tpz. Predict the additional lines, then check and run the file.

Answer

It first prints input: 0. Zero fails the value > 0 condition, so the next line is cannot add task: minutes must be a positive integer. The final line is inspection finished because execution leaves the inspect scope normally.

Continue to the next lesson

You are ready when you can follow "later" from None to Err and find where ? returns it. Check that the added zero input prints its error and cleanup line, and that the corrected array access succeeds.

Continue to First Application to build the study plan across two files. For reference, see Null, Option, Result & Faults and Defer & Resources.