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 не возвращает никакого значения в обычном смысле.

Защищена конструкцией 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

Spec-Zone.ru

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