Program Structure

Defer & Resources

Close a File with using, follow the order of deferred cleanup, and distinguish Result returns from runtime faults.

Close an opened file when its block ends, and check the order of cleanup actions. Use defer to schedule cleanup for a scope, or using to close one File automatically.

Install Topaz with First Program. File access requires an execution environment with filesystem permission.

Watch the cleanup order

Create defer-resources.txt in your working folder. Save it as UTF-8 without a BOM, with ready followed by one LF line ending. The five letters and the newline make six Unicode scalar values.

ready

In the same folder, save this code as defer-resources.tpz.

TOPAZ
function inspectNote(path: string) -> Result<(), string> {
    using file = open(path)? {
        defer { print("body cleanup") }
        let note = file.read()?
        print("note scalars: {note.scalars().length}")
    }

    print("resource scope closed")
    Ok(())
}

match inspectNote("defer-resources.txt") {
    case Ok(_) => print("done")
    case Err(message) => print("error: {message}")
}

Run both commands from that folder.

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

Both commands exit with code 0. The program prints these lines in order.

Output
note scalars: 6
body cleanup
resource scope closed
done

open(path)? opens the file once. After a successful open, using registers a call to close the file. The body then registers its defer action. At block exit, that later action prints body cleanup first. The file closes before resource scope closed is printed.

If the scalar count differs, check the text file encoding and line ending. If opening fails, check that defer-resources.txt exists in the working folder and that you can read it.

Choose where cleanup runs

TaskForm
Run a call or block when leaving the current scopedefer call or defer { ... }
Close one acquired File when its child block endsusing file = open(path)? { ... }
Handle a failed open, read, or writeMatch or return Result, or propagate it with ? from a compatible function

Each defer belongs to the innermost enclosing scope. Actions run in reverse registration order when that scope ends. They also run when return, ?, break, or continue exits the scope.

The using binding cannot be reassigned and is visible inside its body. A closure can retain the File value, but the file still closes when the using block ends. Retaining the value does not keep the file open.

Follow cleanup when an operation fails

FailureWhat happens
open returns Err? returns it before using registers a close
file.read() returns Err? leaves the block, runs its cleanup, and returns the error from inspectNote
An operation raises a runtime faultEvaluation stops, and subsequent defer and using cleanup is unspecified

A fault is an error that stops evaluation, such as an out-of-bounds array access. If a deferred action faults, the runtime decides how to log or collect it. The fault cannot be caught as a value and does not replace an Err already being returned.

Let using close the file once

Acquire a file with open(path). There is no File.open(...) operation. A using statement accepts one bare immutable name and one File.

Let using close its file. Calling file.close() inside the body still leaves the registered close attempt in place. Behavior after close or repeated close is unspecified across execution targets.

Check the supported resource operations

using supports File. Topaz does not define user-supplied disposal protocols, multiple-resource acquisition in one using, or resource transfer. Asynchronous cleanup, cleanup after cancellation or host exceptions, and cleanup at module shutdown are also undefined.

Generated targets must preserve these rules or reject the program before writing build output. Check file operations on the target you intend to use.

For a guided exercise, read Failures and Resources. Compare failure values in Null, Option, Result & Faults, and find File methods in Files & Resources.