メインコンテンツにスキップ
モジュール 3 · レッスン 5 / 5コース内の 15/50~12 min
モジュールのレッスン (5/5)

`defer`: 確実なクリーンアップ

defer は呼び出しの実行を ** 囲んでいる関数が終了するまで延期します 戻ります**。関数がパニックによって終了した場合でも実行されます。複数の defer 同じ関数内では LIFO 順序 (後入れ先出し) で実行されます。

これは、リソースのクリーンアップの慣用的なメカニズムです。つまり、ファイルを閉じます。 ミューテックスのロックを解除し、接続を解放し、状態を復元します。

構文と標準的な使用例

Go
f, err := os.Open(path)
if err != nil {
    return err
}
defer f.Close()   // chiamata SEMPRE all'uscita della funzione

// ... usa f liberamente ...
return nil

通常: f.Close()、mu.Unlock()、db.Close()、tx.Rollback()、 recover()。

引数はすぐに評価され、実行は延期される

defer foo(x) を記述すると、x が実行時に評価されます。 defer、実行時ではありません:

Go
x := 10
defer fmt.Println(x)   // cattura 10
x = 99
// quando la funzione ritorna stampa "10", non "99"

LIFO注文

Defer はスタックされて、最新のものから実行されます。

Go
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
// output: 3, 2, 1

これはリソースのセマンティクスと一致しており、最後に開かれたものが最初に開かれます。 閉店すること。

defer + recover: パニックの処理

defer は、panic をインターセプトする唯一の方法です。

Go
func safe() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("recuperato:", r)
        }
    }()
    panic("boom")
}

落とし穴: ループ内の defer

defer f.Close() をループに入れると、終了まで累積が延期されます。 各反復ではなく、関数。長いループでは多くのファイルを開きます ファイル記述子リークの危険性があるため、関数が終了する場合にのみそれらを閉じます。

Go
// MALE: defer accumulati
for _, path := range paths {
    f, _ := os.Open(path)
    defer f.Close()   // chiude TUTTI alla fine della funzione esterna
    // ...
}

// MEGLIO: estrai in una funzione
for _, path := range paths {
    elabora(path)   // ogni chiamata ha il proprio scope di defer
}

試してみてください

運動#go.m3.l5.e1
試行回数: 0読み込み中…

'inizio' を印刷し、defer で 'fine' の印刷を延期してから、'mezzo' を印刷します。出力は inizio / mezzo /fine である必要があります。

エディターを読み込み中…
ヒントを表示

main() が返ろうとしているときに遅延が実行されます。

3 回の試行後に解決策が利用可能になります

運動#go.m3.l5.e2
試行回数: 0読み込み中…

コード内に 1、2、3 をこの順序で出力する 3 つの連続した defer を記述します。出力は3/2/1(LIFO)となります。

エディターを読み込み中…
ヒントを表示

3 つの延期は順番に行われます。逆の順序で実行されることに注意してください。

3 回の試行後に解決策が利用可能になります

Quiz#go.m3.l5.e3
準備完了

これは何を印刷しますか? (注: 遅延引数はすぐに評価されます)

Go
x := 10
defer fmt.Println(x)
x = 99
回答の選択肢

要約

  • defer call() は、関数が戻るまで実行を延期します。
  • 複数の遅延 = LIFO 注文。
  • 引数はすぐに評価されます、実行は延期されます。
  • ファイル、ミューテックス、接続などの決定的なクリーンアップに使用されます。
  • defer + recover() は、panic をインターセプトします (まれに、境界で)。
  • 決して defer を長いループ内で使用しないでください。外側の関数が戻るまで蓄積されます。