Используйте defer для общей очистки при выходе из области видимости. Для одного принадлежащего области File инструкция using объединяет получение ресурса, использование в дочерней области и очистку через неявное закрытие.
Проверяем порядок очистки
Рядом с программой создайте текстовый файл defer-resources.txt с точным содержимым:
readyСохраните следующий код как defer-resources.tpz:
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: {сообщение}")
}Проверьте и запустите файл:
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 и ошибки, а точные операции с файлами найдите в разделе Файлы и ресурсы.