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

Defer и ресурсы

Закройте File с помощью using, проследите порядок очистки defer и отличите возврат Result от ошибки выполнения.

Закройте открытый файл при выходе из блока и проследите порядок очистки. Используйте defer, чтобы назначить очистку для области видимости, или using, чтобы автоматически закрыть один File.

Установите Топаз по разделу Первая программа. Для работы с файлами среде выполнения нужно разрешение на доступ к файловой системе.

Проверяем порядок очистки

Создайте defer-resources.txt в рабочей папке. Сохраните его в UTF-8 без BOM, добавив после ready один перевод строки LF. Пять букв и перевод строки составляют шесть скалярных значений Unicode.

ready

В той же папке сохраните код в файле defer-resources.tpz.

TOPAZ
function проверитьЗаметку(путь: string) -> Result<(), string> {
    using файл = open(путь)? {
        defer { print("body cleanup") }
        let заметка = файл.read()?
        print("note scalars: {заметка.scalars().length}")
    }

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

match проверитьЗаметку("defer-resources.txt") {
    case Ok(_) => print("done")
    case Err(сообщение) => print("error: {сообщение}")
}

Выполните обе команды из этой папки.

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

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

Вывод
note scalars: 6
body cleanup
resource scope closed
done

open(путь)? открывает файл один раз. После успешного открытия using регистрирует закрытие файла. Затем тело блока регистрирует действие defer. При выходе из блока это более позднее действие первым выводит body cleanup. Файл закрывается перед выводом resource scope closed.

Если число скалярных значений отличается, проверьте кодировку файла и перевод строки. Если открыть файл не удалось, проверьте наличие defer-resources.txt в рабочей папке и разрешение на чтение.

Выбираем место очистки

ЗадачаФорма
Выполнить вызов или блок при выходе из текущей областиdefer call или defer { ... }
Закрыть один открытый File при выходе из дочернего блокаusing файл = open(путь)? { ... }
Обработать неудачное открытие, чтение или записьРазобрать или вернуть Result, либо передать его через ? из совместимой функции

Каждый defer относится к ближайшей окружающей области видимости. При выходе из неё действия выполняются в порядке, обратном регистрации. Они выполняются и при выходе через return, ?, break или continue.

Привязку using нельзя переназначить, и она видна внутри тела блока. Замыкание может сохранить значение File, но файл всё равно закрывается при выходе из блока using. Сохранение значения не оставляет файл открытым.

Следим за очисткой при неудачной операции

ОшибкаЧто происходит
open возвращает Err? возвращает ошибку до регистрации закрытия в using
файл.read() возвращает Err? выходит из блока, выполняет очистку и возвращает ошибку из проверитьЗаметку
Операция вызывает ошибку выполненияВычисление останавливается, а последующая очистка defer и using не определена

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

Поручаем using одно закрытие файла

Открывайте файл через open(path). Операции File.open(...) нет. Инструкция using принимает одно простое неизменяемое имя и один File.

Поручите закрытие файла инструкции using. Вызов файл.close() внутри тела не отменяет зарегистрированную попытку закрытия. Поведение после закрытия или при повторном закрытии не определено для всех целевых сред.

Проверяем поддерживаемые операции с ресурсами

using поддерживает File. Топаз не определяет пользовательские протоколы освобождения, получение нескольких ресурсов в одном using или передачу ресурса. Также не определены асинхронная очистка, очистка после отмены или исключений хоста и очистка при завершении модуля.

Код для целевой среды должен сохранять эти правила, иначе компилятор должен отклонить программу до записи результата сборки. Проверьте файловые операции в выбранной целевой среде.

Пошаговый пример есть в уроке Ошибки и ресурсы. Сравните значения ошибок в разделе Null, Option, Result и ошибки, а методы File найдите в разделе Файлы и ресурсы.