Spec-Zone.ru › Elisp

Очистка после выхода из области видимости

Конструкт 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API