Выберите, как программа обрабатывает отсутствие значения и неудачную операцию. Проследите путь входных данных от None до Err, затем исправьте обращение к массиву, которое останавливает выполнение.
Установите Топаз по разделу Первая программа. Пошаговое знакомство с этими примерами есть в уроке Ошибки и ресурсы.
Обрабатываем неверный ввод через Result
Сохраните код в файле failures-resources.tpz.
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")В этой папке проверьте и запустите файл.
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 finishedOption хранит значение в Some или обозначает его отсутствие через None. Здесь toInt("25") возвращает Some(25), а toInt("later") возвращает None.
Result хранит успешный результат в Ok или значение ошибки в Err. Функция разобратьМинуты принимает положительные целые числа. При отсутствии числа, нуле или отрицательном значении она возвращает Err с сообщением, показанным выше.
В создатьЗадачу постфиксный ? извлекает целое число из Ok. Получив Err, он сразу возвращает эту ошибку из создатьЗадачу. Последний match выводит задачу или сообщение об ошибке. Чтобы второй ввод прошёл проверку, замените "later" строкой с положительным целым числом, например "10".
Выбираем представление
| Ситуация | Что использовать или ожидать |
|---|---|
| Значение может отсутствовать | Option<T> с Some(value) или None |
Поле внешних данных допускает null | T | null |
| Вызывающему коду нужен результат или объяснение ошибки | Result<T, E> с Ok(value) или Err(error) |
| Неверная операция останавливает выполнение, например выход за границы массива | Ошибка выполнения, называемая fault |
Используйте Option для ожидаемого отсутствия значения в API Топаза. Используйте null, когда он встречается во внешних данных, с которыми вы работаете.
Получаем значение или подставляем запасное
| Тип входного значения | Выражение | Результат |
|---|---|---|
maybeUser: Option<{ name: string }> | maybeUser?.name | Some(name) или None |
| Тот же тип | maybeUser?.name ?? "guest" | Имя, а при его отсутствии "guest" |
configPath: string | null | configPath ?? "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.
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 и ресурсы. Другие операции, вызывающие ошибки выполнения, перечислены в разделе Поведение среды выполнения.