🔐

Goのdefer(遅延実行)入門:解説と活用方法

2025/02/18に公開

はじめに

Go(Golang)には便利な機能としてdefer(遅延実行)があります。
deferを使うことで、関数の終了時に特定の処理を実行することができ、コードの可読性やメンテナンス性を向上させることができます。


deferとは?

deferは、関数の実行が終了する直前に、指定した処理を遅延実行するためのキーワードです。

deferの基本構文

func main() {
    defer fmt.Println("This will be printed last")
    fmt.Println("This will be printed first")
}

実行結果

This will be printed first
This will be printed last
  • defer fmt.Println("This will be printed last") は main() 関数の終了直前に実行される。
  • deferを使うと、関数の終了時に確実に実行したい処理を記述できる。

deferの主な活用方法

1. ファイルのクローズ処理

ファイルを開いた後、クローズ処理を忘れるとリソースリークにつながる可能性があります。
deferを使うことで、関数の終了時に確実にCloseを呼び出すことができます。

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("example.txt")
    if err != nil {
        fmt.Println("Error opening file:", err)
        return
    }
    defer file.Close() // 関数終了時にファイルを閉じる
    
    fmt.Println("File opened successfully")
}
  • defer file.Closeにより、関数の終了時にファイルを確実に閉じる。
  • deferを使わない場合、エラーハンドリングによってCloseを呼び忘れるリスクがある。

2. データベース接続の管理

データベース接続も適切にCloseする必要があります。

package main

import (
    "database/sql"
    "fmt"
    _ "github.com/lib/pq" // PostgreSQLのドライバ
)

func main() {
    db, err := sql.Open("postgres", "user=username dbname=mydb sslmode=disable")
    if err != nil {
        fmt.Println("Error connecting to database:", err)
        return
    }
    defer db.Close() // 関数終了時に接続を閉じる
    
    fmt.Println("Connected to database")
}
  • defer db.Closeにより、関数終了時に確実にデータベース接続を閉じる。
  • クリーンアップ処理を一箇所にまとめられるため、コードが簡潔になる。

3. Mutexのロック解除

Goのsync.Mutexを使用する場合、ロックを解除し忘れるとデッドロックが発生する可能性があります。
deferを使えば、確実にロックを解除できます。

package main

import (
    "fmt"
    "sync"
)

var mu sync.Mutex

func criticalSection() {
    mu.Lock()
    defer mu.Unlock() // 関数終了時にロックを解除
    fmt.Println("Critical section")
}

func main() {
    criticalSection()
}
  • defer mu.Unlock を使うことで、関数終了時にロックが解除されるため、デッドロックを防げる。
  • deferを使わない場合、関数内の条件分岐などによって Unlock()の呼び忘れが発生する可能性がある。

4. 複数のdeferを使う場合(LIFO順)

Goのdeferは LIFO(Last In, First Out) の順序で実行されます。

package main

import "fmt"

func main() {
    defer fmt.Println("Third")
    defer fmt.Println("Second")
    defer fmt.Println("First")
}

実行結果

First
Second
Third
  • deferはスタックのように積み重なり、最後に追加されたものから実行される。
  • クリーンアップ処理を順番に実行する場合に役立つ。

まとめ

概念 説明
deferとは? 関数の終了直前に実行される処理を登録するキーワード
活用例 ファイルクローズ、データベース接続管理、Mutexロック解除など
複数のdefer LIFO(Last In, First Out)の順序で実行される

Discussion