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

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

Выберите Option или Result для отсутствующих значений и ошибок ввода, используйте null для внешних данных и исправляйте ошибки выполнения.

Выберите, как программа обрабатывает отсутствие значения и неудачную операцию. Проследите путь входных данных от None до Err, затем исправьте обращение к массиву, которое останавливает выполнение.

Установите Топаз по разделу Первая программа. Пошаговое знакомство с этими примерами есть в уроке Ошибки и ресурсы.

Обрабатываем неверный ввод через Result

Сохраните код в файле failures-resources.tpz.

TOPAZ
record УчебнаяЗадача {
    название: string,
    минуты: int,
}

function разобратьМинуты(текст: string) -> Result<int, string> {
    match toInt(текст) {
        case Some(значение) if значение > 0 => Ok(значение)
        case _ => Err("minutes must be a positive integer")
    }
}

function создатьЗадачу(название: string, текстМинут: string) -> Result<УчебнаяЗадача, string> {
    let минуты = разобратьМинуты(текстМинут)?
    Ok(УчебнаяЗадача { название: название, минуты: минуты })
}

function проверить(текстМинут: string) -> () {
    print("input: {текстМинут}")
    defer { print("inspection finished") }
    match создатьЗадачу("Build application", текстМинут) {
        case Ok(задача) => print("ready: {задача.название}, {задача.минуты} minutes")
        case Err(сообщение) => print("cannot add task: {сообщение}")
    }
}

проверить("25")
проверить("later")

В этой папке проверьте и запустите файл.

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

Обе команды завершаются с кодом 0. Программа выводит сообщение об ошибке для "later" и завершает проверку.

Вывод
input: 25
ready: Build application, 25 minutes
inspection finished
input: later
cannot add task: minutes must be a positive integer
inspection finished

Option хранит значение в Some или обозначает его отсутствие через None. Здесь toInt("25") возвращает Some(25), а toInt("later") возвращает None.

Result хранит успешный результат в Ok или значение ошибки в Err. Функция разобратьМинуты принимает положительные целые числа. При отсутствии числа, нуле или отрицательном значении она возвращает Err с сообщением, показанным выше.

В создатьЗадачу постфиксный ? извлекает целое число из Ok. Получив Err, он сразу возвращает эту ошибку из создатьЗадачу. Последний match выводит задачу или сообщение об ошибке. Чтобы второй ввод прошёл проверку, замените "later" строкой с положительным целым числом, например "10".

Выбираем представление

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

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

Получаем значение или подставляем запасное

Тип входного значенияВыражениеРезультат
maybeUser: Option<{ name: string }>maybeUser?.nameSome(name) или None
Тот же типmaybeUser?.name ?? "guest"Имя, а при его отсутствии "guest"
configPath: string | nullconfigPath ?? "default.toml"Путь, а при null строка "default.toml"

?. обращается к полю или вызывает метод, когда значение присутствует. При отсутствии значения он возвращает None для Option и null для типа, допускающего null. Если член уже возвращает Option, оператор ?. сохраняет одну обёртку Option вокруг результата.

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

target ??= value присваивает значение, когда слева находится None или null. Цель присваивания должна быть изменяемой. Для Option<int> записывайте присваиваемое значение как Some(42). Топаз не добавляет Some автоматически.

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

Находим и исправляем ошибку выполнения

Сохраните эту программу в отдельном файле intentional-fault.tpz.

TOPAZ
let задачи = ["Run first program", "Build application"]
print(задачи[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(задачи[2])
  |       ^^^^^^^^^

У массива есть индексы 0 и 1. В строке 2 замените задачи[2] на задачи[1] и запустите файл снова. Он выведет Build application и завершится с кодом 0.

Ошибка выполнения останавливает вычисление, не возвращая None или Err. Операторы ?, ??, ?. и выражение concurrent её не перехватывают. Неверные операции с константами могут быть отклонены при проверке, ещё до запуска.

Передаём ошибки и выполняем очистку

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

Порядок очистки описан в разделе Defer и ресурсы. Другие операции, вызывающие ошибки выполнения, перечислены в разделе Поведение среды выполнения.