cpp

Инструкция defer

Инструкция defer позволяет вызвать функцию после выхода потока управления из области видимости даже в случае возникновения ошибки. Если инструкций несколько, то функции вызываются в обратной последовательности. Когда нам это может понадобиться? Например, при работе с файлами. Помните, что файл должен быть обязательно закрыт после завершения работы с файлом. Мы можем закрыть файл явным образом в конце, но во время выполнения промежуточных инструкций возможна ошибка. Поток управления в этом случае не дойдет до инструкции закрытия файла и файл останется открытым. Так вот при ошибке функция, указанная в инструкции defer, все равно будет вызвана, а значит файл будет закрыт.

Обратите внимание

Пример использования инструкции defer приведен в листинге 10.17.

Листинг 10.17. Инструкция defer

package main

import "fmt"

func main() {
   defer test1()
   defer test2()
   fmt.Println("Начало функции main()")
   panic("Описание ошибки") // Имитация ошибки
   // Эта инструкция выполнена не будет!
   fmt.Println("Конец функции main()")
}
func test1() {
   fmt.Println("test1()")
}
func test2() {
   fmt.Println("test2()")
}

Результат выполнения в окне консоли:

Начало функции main()
test2()
test1()
panic: Описание ошибки

goroutine 1 [running]:
main.main()
        C:/book/test/main.go:9 +0x98
exit status 2

Обратите внимание: инструкция

fmt.Println("Конец функции main()")

не была выполнена, т. к. перед ней мы вызвали функцию panic(), которая прерывает работу программы и выводит сообщение об ошибке. Заметьте, что функции, указанные в инструкциях defer, были выполнены.

При использовании инструкции defer следует учитывать еще следующие нюансы:

  • сохраняется значение переменной до инструкции defer, если переменная указана в качестве параметра:
func test1() {
   x := 1
   defer func(a int) {
      fmt.Println("defer a =", a) // defer a = 1
   }(x)
   x = 2                          // Изменяем значение
   fmt.Println("x =", x)          // x = 2
}

Если выполняется захват контекста, то получим значение переменной при вызове отложенной функции:

func test2() {
   x := 1
   defer func() {
      fmt.Println("defer x =", x) // defer x = 2
   }()
   x = 2                          // Изменяем значение
   fmt.Println("x =", x)          // x = 2
}
  • внутри отложенной функции есть доступ к именованным возвращаемым значениям внешней функции, поэтому мы можем изменить возвращаемое значение уже после вызова инструкции return:
func test3() (n int) {
   defer func() {
      n = 10
   }()
   return 0
}

Вызовем функцию test3() и посмотрим на результат:

fmt.Println(test3()) // 10, а не 0

Учебник Go (Golang)
Учебник Go (Golang) в формате PDF

Помощь сайту

ЮMoney (Yandex-деньги): 410011140483022

ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов

cpp