遅延実行(defer文)

defer文は、関数の終了まで実行を延期するものです。 関数終了時に必ず行う処理を登録します。

なぜdefer文が必要か

defer文が必要な理由は次の通りです。

  • 関数の終了処理を漏れなく行うため
  • 関数の終了処理を複数箇所に分散させないため
  • 開始処理と終了処理をセットで記載するとコードが読み易くなるため

defer文を使うかどうかでコードがどう変わるかを見てみましょう。

次のサンプルはdefer文を使わないものです。

package main

import (
    "fmt"
    "os"
)

func main() {
    readFile("/proc/cpuinfo")
}

func readFile(filepath string) {
    f, err := os.Open(filepath)
    if err != nil {
        fmt.Println(err)
        return
    }

    buf := make([]byte, 64)
    for {
        n, err := f.Read(buf)
        if n == 0 {
            break
        }
        if err != nil {
            fmt.Println(err)
            f.Close()
            return
        }
        fmt.Print(string(buf[:n]))
    }
    fmt.Println("readFile関数は正常に処理しました。")
    f.Close()
}

defer文を使うと次の通りです。defer文の書き方は以降で説明します。

package main

import (
    "fmt"
    "os"
)

func main() {
    readFile("/proc/cpuinfo")
}

func readFile(filepath string) {
    f, err := os.Open(filepath)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer f.Close()

    buf := make([]byte, 64)
    for {
        n, err := f.Read(buf)
        if n == 0 {
            break
        }
        if err != nil {
            fmt.Println(err)
            return
        }
        fmt.Print(string(buf[:n]))
    }
    fmt.Println("readFile関数は正常に処理しました。")
}

defer文の書き方

キーワードdeferを使って、関数終了時に実行する式を追加できます。

defer文に書いた関数の引数は即時評価されますが、defer文に書いた関数の実行は、外側の関数が終了する直前まで延期されます。

複数追加した場合、最後に追加されたものから順に実行されます。

func main() {
    fmt.Println(1)
    i := 11
    // 引数は即時評価
    defer fmt.Println(i)
    i = 111
    defer fmt.Println(12)
    fmt.Println(2)
}
// 実行結果:
// 1
// 2
// 12
// 11