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

Defer и ресурсы

Выбор между defer и формой using для File, порядок очистки и границы ошибок выполнения.

Используйте defer для общей очистки при выходе из области видимости. Для одного принадлежащего области File инструкция using объединяет получение ресурса, использование в дочерней области и очистку через неявное закрытие.

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

Рядом с программой создайте текстовый файл defer-resources.txt с точным содержимым:

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

Строки появляются в следующем порядке:

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

Вызов open(path)? делает одну попытку получить файл. Инструкция using регистрирует неявное закрытие лишь после успешного получения ресурса. Действие defer, добавленное позже в теле блока, выполняется первым, а к выводу строки resource scope closed программа переходит уже после неявного закрытия ресурса.

Выбираем владельца времени жизни

СитуацияЧто использовать
Один вызов или блок очистки относится к текущей области видимостиdefer call или defer { ... }
Один File принадлежит одному дочернему блокуusing file = open(path)? { ... }
Получение, чтение или запись могут штатно завершиться неудачейВернуть или разобрать Result; применять ? только в совместимой функции
Программа нарушила контракт среды выполненияСчитать это ошибкой выполнения, а не восстанавливаемой ветвью очистки

Несколько действий defer в одной области видимости выполняются в порядке, обратном регистрации. Очистка также происходит при выходе из области через return, постфиксный ?, break или continue. Имя в using неизменяемо и видно только внутри тела блока. Замыкание может сохранить значение File, но неявное закрытие всё равно происходит при выходе из области using. Сохранение значения не продлевает время жизни ресурса.

Передача Result — не ошибка выполнения

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

Ошибка выполнения немедленно прерывает вычисление. Лексические действия defer и очистка using после этого момента не специфицированы. Ошибка внутри отложенного действия подчиняется политике журналирования и сбора ошибок среды выполнения. Она не становится перехватываемым значением и оставляет уже возвращаемый Err без изменений.

Частое исправление

Не пишите получение файла как File.open(...) и не приписывайте using универсальный протокол ресурсов. Точная функция — open(path), а инструкция using принимает одно простое неизменяемое имя и один File.

Явный вызов close() внутри тела using не отменяет зарегистрированную попытку закрытия. Поведение после закрытия и при повторном закрытии не является переносимым, поэтому обычный код должен оставлять одно закрытие одному владельцу.

Точная граница

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

Вернитесь к учебному разбору на странице Ошибки и ресурсы, сравните отсутствие значения и восстанавливаемую ошибку на странице Null, Option, Result и ошибки, а точные операции с файлами найдите в разделе Файлы и ресурсы.