defer
в Go — это механизм для отложенного выполнения функции или метода до момента завершения текущей функции. Основные цели:
func readFile(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close() // Закрытие файла при выходе из функции
// Работа с файлом
return nil
}
Вызовы defer
выполняются в порядке LIFO (Last In, First Out):
func example() {
defer fmt.Println("First")
defer fmt.Println("Second")
defer fmt.Println("Third")
}
// Выведет:
// Third
// Second
// First
Аргументы отложенной функции вычисляются в момент объявления defer:
func printTime() {
now := time.Now()
defer fmt.Println(now) // Зафиксирует текущее время
time.Sleep(2 * time.Second)
// Выведет время до sleep, а не после
}
defer
может изменять именованные возвращаемые значения:
func double(x int) (result int) {
defer func() { result *= 2 }()
return x
}
// double(3) вернет 6
func connectToDB() (*sql.DB, error) {
db, err := sql.Open("driver", "connection")
if err != nil {
return nil, err
}
defer func() {
if err != nil {
db.Close() // Закрываем при ошибке
}
}()
err = db.Ping()
if err != nil {
return nil, err
}
return db, nil
}
func longOperation() {
defer func(start time.Time) {
fmt.Printf("Operation took %v\n", time.Since(start))
}(time.Now())
// Длительная операция
}
func safeCall() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered:", r)
}
}()
panic("something went wrong")
}
func processTempFile() error {
tmpfile, err := os.CreateTemp("", "example")
if err != nil {
return err
}
defer os.Remove(tmpfile.Name()) // Удаление файла после использования
defer tmpfile.Close()
// Работа с временным файлом
return nil
}
defer
работает только в рамках текущей функции:
func main() {
defer fmt.Println("Main done")
func() {
defer fmt.Println("Inner done")
}()
}
// Выведет:
// Inner done
// Main done
В циклах defer
выполняется при выходе из каждой итерации:
for _, file := range files {
f, err := os.Open(file)
if err != nil {
return err
}
defer f.Close() // Может привести к утечке файловых дескрипторов
// Лучше использовать отдельную функцию
}
defer
имеет небольшие накладные расходы. В критичных к производительности местах лучше использовать явное управление:
// Быстрее:
func fast() {
f := acquire()
// работа
release(f)
}
// Медленнее:
func slow() {
f := acquire()
defer release(f)
// работа
}
defer func() {
err := file.Close()
if err != nil {
log.Printf("close error: %v", err)
}
}()
for {
conn := acceptConnection()
defer conn.Close() // Утечка памяти!
// Лучше: сразу закрывать или использовать отдельную функцию
}
defer mutex.Unlock() // Разблокировка перед блокировкой!
defer mutex.Lock()
defer
гарантирует выполнение кода при выходе из функцииПравильное использование defer
делает код Go более надежным и читаемым, особенно при работе с ресурсами и ошибками.