Очистка при нелокальных выходах
Конструкция unwind-protect необходима всякий раз, когда вы временно приводите структуру данных в несогласованное состояние; она позволяет снова привести данные в согласованное состояние в случае ошибки или выброса исключения. (Другая, более специализированная конструкция очистки, используемая только при изменении содержимого буфера, — это группа атомарных изменений; Группы атомарных изменений.)
- Специальная форма: unwind-protect body-form cleanup-forms…
-
unwind-protectвыполняет body-form, гарантируя, что cleanup-forms будут вычислены, если управление покинет body-form, независимо от того, как это произойдёт. body-form может завершиться нормально, выполнитьthrowизunwind-protectили вызвать ошибку; во всех случаях будут вычислены cleanup-forms.Если body-form завершается нормально,
unwind-protectвозвращает значение body-form после вычисления cleanup-forms. Если body-form не завершается,unwind-protectне возвращает никакого значения в обычном смысле.Защищена конструкцией
unwind-protectтолько форма body-form. Если какая-либо из форм cleanup-forms сама завершает выполнение нелокально (с помощьюthrowили ошибки),unwind-protectне гарантирует вычисление остальных форм. Если сбой одной из форм cleanup-forms может привести к проблемам, защитите её, поместив эту форму в другую конструкциюunwind-protect.
Например, здесь мы создаём невидимый буфер для временного использования и гарантируем, что он будет уничтожен перед завершением:
(let ((buffer (get-buffer-create " *temp*")))
(with-current-buffer buffer
(unwind-protect
body-form
(kill-buffer buffer))))
Можно подумать, что мы могли бы просто написать (kill-buffer
(current-buffer)) и обойтись без переменной buffer. Однако приведённый выше способ безопаснее, если в body-form произойдёт ошибка после переключения на другой буфер! (В качестве альтернативы можно поместить body-form в save-current-buffer, чтобы гарантировать, что временный буфер снова станет текущим и его можно будет уничтожить.)
В Emacs есть стандартный макрос с именем with-temp-buffer, который раскрывается примерно в код, показанный выше (см. Текущий буфер). Несколько макросов, определённых в этом руководстве, используют unwind-protect таким образом.
Вот реальный пример из пакета FTP. Он создаёт процесс (см. Процессы), чтобы попытаться установить соединение с удалённой машиной. Поскольку функция ftp-login очень подвержена многочисленным проблемам, которые автор функции не может предвидеть, она защищена формой, гарантирующей удаление процесса в случае сбоя. Иначе Emacs мог бы заполниться бесполезными подпроцессами.
(let ((win nil))
(unwind-protect
(progn
(setq process (ftp-setup-buffer host file))
(if (setq win (ftp-login process host user password))
(message "Logged in")
(error "Ftp login failed")))
(or win (and process (delete-process process)))))
В этом примере есть небольшой недочёт: если пользователь нажмёт C-g, чтобы прервать выполнение, и прерывание произойдёт сразу после возврата функции ftp-setup-buffer, но до присваивания значения переменной process, процесс не будет уничтожен. Исправить этот недочёт непросто, но, по крайней мере, он крайне маловероятен.
Copyright © 1990-1996, 1998-2026 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Cleanups.html