ProgrammingGo開発者

Goにおけるdeferの動作は、returnやpanicとの相互作用においてどのように機能するのか、defer内で戻り値を変更することがどのように危険であるかについて説明してください。

Hintsage AIアシスタントで面接を突破

答え。

歴史的に、deferの概念はリソースの安全な解放や関数の実行結果に関係なくアクションを完了するためにGoに導入されました(通常の終了やpanicによる終了に関わらず)。しかし、deferとreturn、およびpanicの相互作用には、経験豊富な開発者でさえ見落としがちな不都合な罠がいくつか存在します。

問題は、戻り値の計算順序、名前付き戻り値の動作、defer内でのこれらの値の変更が多くの言語の一般的な動作とは大きく異なることです。さらに、defer内で既に計算された値を変更しようとすると予期しない動作を引き起こす場合があります。

解決策は、常に覚えておくべきです:関数が返す値はdeferが実行される前に計算されますが、名前付き結果を使用している場合、それらはdefer内で関数から実際に戻る前に変更できます。

コードの例:

func tricky() (res int) { defer func() { res = 42 // 戻り値を変更します! }() return 10 } func main() { fmt.Println(tricky()) // 10ではなく42と表示されます }

主な特徴:

  • deferは、return引数の計算の後、関数からの実際の戻りの前に常に実行されます。
  • defer内で名前付き戻り値を変更すると、戻り値に影響します。
  • panicが発生した場合、すべてのdeferされた関数はrecoverに移行する前、またはプログラムが終了する前に実行されます。

意外な質問。

defer関数はどのような順序で実行されますか?

それらは宣言された順序の逆(スタック — LIFO)で実行されます。

func f() { defer fmt.Println("1") defer fmt.Println("2") } // 出力: 2、その後1

defer関数のパラメータは、deferを宣言した時点で計算されるのか、それとも実行時に計算されるのか?

defer関数のパラメータは、deferを宣言した時点で計算され、呼び出し時ではありません。

func f() { i := 1 defer fmt.Println(i) // 1が表示されます。たとえiがその後変更されても i = 2 }

deferは無名の結果を変更できますか?

いいえ。deferで変更できるのは名前付き戻り値のみです。

func f() int { defer func() { /* 何も変更しない */ }() return 5 }

一般的なミスとアンチパターン

  • deferを通じて無名(非名称付き)結果の変更を期待すること
  • 不要にdeferを使用してreturn値を変更し、不確実な動作や複雑なバグを引き起こすこと
  • deferにおけるパラメータの計算と渡し方の順序を無視すること

実生活の例

ネガティブなケース

若手開発者がdeferを使用して戻り値をログに記録しようとしたが、間違って名前付き戻り値を変更してしまい、正確な関数の結果を"上書き"してしまった。

利点:

  • ロジックのエラーの迅速な修正

欠点:

  • 不正確な値の返却、デバッグが困難

ポジティブなケース

別の状況では、deferはリソースの解放やログ記録のためだけに使用され、returnを変更せず、重要な値はreturnの前に明示的に設定されました。

利点:

  • 透明性、予測可能な動作

欠点:

  • 出口段階で何らかの副作用が必要な場合、追加のコード行を明示的に追加する必要があります。