Структура программы

Null, Option, Result и ошибки

Выбор представления для обычного отсутствия, nullable-данных, восстанавливаемой ошибки и нарушения контракта времени выполнения.

Обращайтесь к этой странице, когда значение может отсутствовать или операция может завершиться неудачно. Сначала спросите не «какой оператор короче?», а «может ли вызывающая сторона продолжить работу?».

Выбирайте по смыслу

СитуацияЧто использовать
Отсутствие значения — обычный исход внутри ТопазаOption<T> с Some(value) или None
Внешние данные явно содержат nullable-полеT | null
Вызывающая сторона может обработать осмысленную ошибкуResult<T, E> с Ok(value) или Err(error)
Программа нарушила контракт времени выполненияСбой (fault): вычисление прерывается и не создаёт значение

Для обычного отсутствия в API Топаза предпочитайте Option. Оставляйте null для данных и границ интеграции, где он действительно присутствует. Используйте Result, когда вызывающей стороне нужны причина ошибки или выбор способа восстановления.

Превратите неверный ввод в восстанавливаемый результат

Эта полная программа превращает отсутствие, возвращённое toInt, в Result с понятным сообщением:

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

Проверьте и запустите программу:

BASH
topaz check failures-resources.tpz
topaz run failures-resources.tpz
Вывод
input: 25
ready: Build application, 25 minutes
inspection finished
input: later
cannot add task: minutes must be a positive integer
inspection finished

toInt возвращает Option<int>, потому что у текста, который не является целым числом, нет разобранного значения. parseMinutes решает, что это отсутствие важно вызывающей стороне, и превращает его в Err. В makeTask постфиксный ? извлекает значение из Ok, а при Err немедленно возвращает ошибку из совместимой функции с результатом Result. Последний match явно обрабатывает оба исхода.

Работайте с необязательными и nullable-значениями

Необязательный доступ и значение по умолчанию сохраняют различие моделей:

  • Если maybeUser: Option<{ name: string }>, то результат maybeUser?.name также остаётся Option, а maybeUser?.name ?? "guest" даёт string.
  • Если configPath: string | null, выражение configPath ?? "default.toml" использует запасное значение только при null.
  • target ??= value — инструкция, которая присваивает значение только пустой цели. Для цели Option<int> требуется Some(42), а не просто 42: Топаз не добавляет Some автоматически.

Операторы ?. и ?? вычисляют левую часть ровно один раз. ?. также не вычисляет аргументы метода, если получатель пуст. Каждый оператор снимает или уплощает один необязательный слой и не превращает Option в null.

Исправление частой ошибки: для значения, которое не является необязательным или nullable, используйте обычную точку .. Если тип сочетает Option<T> и null, явно разберите случаи через match, а не наслаивайте сокращения.

Сбой не является значением ошибки

Эта программа обращается к несуществующему элементу массива:

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

Команда topaz run intentional-fault.tpz завершается с ненулевым кодом:

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

Выход за границы — это сбой (fault): он прерывает вычисление и не создаёт ни None, ни Err. Постфиксный ?, оператор ??, необязательная цепочка и concurrent не перехватывают и не преобразуют сбои. Некоторые неверные операции с константами проверка отклоняет ещё до запуска.

Точные границы

  • Постфиксный ? разрешён только в функции или замыкании, чей возвращаемый тип может нести то же значение ошибки.
  • Общий try, исключения, перехват сбоев, ключевые слова panic и автоматические повторы не являются текущими формами Топаза.
  • assert(...) доступна только в явно выбранном коде test-profile; это не инструкция проверки для обычной программы.
  • Очистку при обычном выходе из области выполняет defer. Граница очистки при сбое уже, поэтому сбой нельзя считать восстанавливаемым Result.

Продолжите с пошаговым уроком Ошибки и ресурсы, точными правилами Defer и ресурсы и границей сбоев на странице Поведение среды выполнения.