モジュールのレッスン (5/5)
`defer`: 確実なクリーンアップ
defer は呼び出しの実行を ** 囲んでいる関数が終了するまで延期します
戻ります**。関数がパニックによって終了した場合でも実行されます。複数の defer
同じ関数内では LIFO 順序 (後入れ先出し) で実行されます。
これは、リソースのクリーンアップの慣用的なメカニズムです。つまり、ファイルを閉じます。 ミューテックスのロックを解除し、接続を解放し、状態を復元します。
構文と標準的な使用例
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、実行時ではありません:
x := 10
defer fmt.Println(x) // cattura 10
x = 99
// quando la funzione ritorna stampa "10", non "99"LIFO注文
Defer はスタックされて、最新のものから実行されます。
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
// output: 3, 2, 1これはリソースのセマンティクスと一致しており、最後に開かれたものが最初に開かれます。 閉店すること。
defer + recover: パニックの処理
defer は、panic をインターセプトする唯一の方法です。
func safe() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recuperato:", r)
}
}()
panic("boom")
}落とし穴: ループ内の defer
defer f.Close() をループに入れると、終了まで累積が延期されます。
各反復ではなく、関数。長いループでは多くのファイルを開きます
ファイル記述子リークの危険性があるため、関数が終了する場合にのみそれらを閉じます。
// 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
}試してみてください
'inizio' を印刷し、defer で 'fine' の印刷を延期してから、'mezzo' を印刷します。出力は inizio / mezzo /fine である必要があります。
ヒントを表示
main() が返ろうとしているときに遅延が実行されます。
3 回の試行後に解決策が利用可能になります
コード内に 1、2、3 をこの順序で出力する 3 つの連続した defer を記述します。出力は3/2/1(LIFO)となります。
ヒントを表示
3 つの延期は順番に行われます。逆の順序で実行されることに注意してください。
3 回の試行後に解決策が利用可能になります
これは何を印刷しますか? (注: 遅延引数はすぐに評価されます)
x := 10
defer fmt.Println(x)
x = 99要約
defer call()は、関数が戻るまで実行を延期します。- 複数の遅延 = LIFO 注文。
- 引数はすぐに評価されます、実行は延期されます。
- ファイル、ミューテックス、接続などの決定的なクリーンアップに使用されます。
defer+recover()は、panicをインターセプトします (まれに、境界で)。- 決して
deferを長いループ内で使用しないでください。外側の関数が戻るまで蓄積されます。