Очистка после выхода из области видимости
Конструкт 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не возвращает никакого значения в обычном смысле.Только body-form защищается
unwind-protect. Если какой-либо из cleanup-forms сам выходит из области видимости (черезthrowили ошибку),unwind-protectне гарантирует вычисления остальных из них. Если неудача одного из cleanup-forms может вызвать проблемы, защитите его другимunwind-protectвокруг этого элемента.Количество активных
unwind-protectформ вместе с количеством привязок локальных переменных учитывается по отношению к пределуmax-specpdl-size(см. Локальные переменные).
Например, здесь мы создаём невидимый буфер для временного использования и гарантируем его уничтожение перед завершением:
(let ((buffer (get-buffer-create " *temp*")))
(with-current-buffer buffer
(unwind-protect
body-form
(kill-buffer buffer))))
Можно было бы просто написать (kill-buffer
(current-buffer)) и обойтись без переменной buffer. Однако показанный выше способ безопаснее, если body-form получит ошибку после переключения на другой буфер! (В качестве альтернативы можно написать save-current-buffer вокруг body-form, чтобы гарантировать, что временный буфер станет текущим вовремя, чтобы его уничтожить.)
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-2022 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Cleanups.html