サーチ…


前書き

defer文は、関数呼び出しをリストにプッシュします。保存された呼び出しのリストは、周囲の関数が返った後に実行されます。 Deferは、さまざまなクリーンアップ処理を実行する機能を単純化するために一般的に使用されます。

構文

  • defer someFunc(args)
  • defer func(){//コードはここに}}()

備考

Deferは、現在実行中の関数の下の呼び出しスタックに新しいスタックフレーム( deferキーワードの後に​​呼び出される関数)を挿入deferによって動作します。これは、スタックが解かれる限り、deferが実行されることを保証します(プログラムがクラッシュしたり、 SIGKILL取得した場合、遅延は実行されません)。

遅延の基礎

Goでのdefer文は、後で実行されるとマークされた単なる関数呼び出しです。 Defer文は、キーワードdefer前に付けた通常の関数呼び出しです。

defer someFunction()

defer文を含む関数が返されると、遅延関数が実行されます。遅延関数への実際の呼び出しは、包含関数が以下の場合に発生します。

  • return文を実行する
  • 終わりから落ちる
  • パニック

例:

func main() {
    fmt.Println("First main statement")
    defer logExit("main") // position of defer statement here does not matter
    fmt.Println("Last main statement")
}

func logExit(name string) {
    fmt.Printf("Function %s returned\n", name)
}

出力:

First main statement
Last main statement
Function main returned

ある関数に複数の遅延ステートメントがある場合、それらはスタックを形成します。最後のdeferは、囲み関数が返った後に実行する最初のdeferあり、その後に先行deferの呼び出しが順番に続きます(以下の例では、パニックを引き起こします)。

func main() {
    defer logNum(1)
    fmt.Println("First main statement")
    defer logNum(2)
    defer logNum(3)
    panic("panic occurred")
    fmt.Println("Last main statement") // not printed
    defer logNum(3) // not deferred since execution flow never reaches this line
}

func logNum(i int) {
    fmt.Printf("Num %d\n", i)
}

出力:

First main statement
Num 3
Num 2
Num 1
panic: panic occurred

goroutine 1 [running]:
....

延期機能は、一度にその引数が評価されていることに注意してくださいdefer実行されます。

func main() {
    i := 1
    defer logNum(i) // deferred function call: logNum(1)
    fmt.Println("First main statement")
    i++
    defer logNum(i) // deferred function call: logNum(2)
    defer logNum(i*i) // deferred function call: logNum(4)
    return // explicit return
}

func logNum(i int) {
    fmt.Printf("Num %d\n", i)
}

出力:

First main statement
Num 4
Num 2
Num 1

関数が名前付きの戻り値を持つ場合、その関数内の遅延匿名関数は、関数が返された後でも戻り値にアクセスして更新できます。

func main() {
    fmt.Println(plusOne(1)) // 2
    return
}

func plusOne(i int) (result int) { // overkill! only for demonstration
    defer func() {result += 1}() // anonymous function must be called by adding ()

    // i is returned as result, which is updated by deferred function above
    // after execution of below return
    return i
}

最後に、 deferステートメントは一般的によく一緒に行われる操作です。例えば:

  • ファイルを開いて閉じる
  • 接続と切断
  • ミューテックスのロックとロック解除
  • waitgroupをdoneとしてマークする( defer wg.Done()

この使用により、実行の流れに関係なく、システムリソースが適切に解放されます。

resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close() // Body will always get closed

遅延関数呼び出し

遅延関数呼び出しは、Javaのような言語のfinallyブロックのようなものに似た目的を果たします。つまり、エラーが発生したかどうかにかかわらず、複数の戻り値を持つ場合にreturn文がヒットしたかどうかにかかわらず、これは、ネットワーク接続やファイルポインタのように閉じなければならないリソースをクリーンアップする場合に便利です。 deferキーワードは、新しいゴルーチンを開始するgoキーワードと同様に、遅延関数呼び出しを示します。 go呼び出しと同様に、関数の引数はすぐに評価されますが、 go呼び出しとは異なり、遅延関数は同時に実行されません。

func MyFunc() {
    conn := GetConnection()    // Some kind of connection that must be closed.
    defer conn.Close()        // Will be executed when MyFunc returns, regardless of how.
    // Do some things...
    if someCondition {
        return                // conn.Close() will be called
    }
    // Do more things
}// Implicit return - conn.Close() will still be called

使用に注意してくださいconn.Close()の代わりにconn.Close -あなただけの機能に渡していない、あなたはその引数を含む完全な関数呼び出しを 、延期しています。複数の関数呼び出しを同じ外​​部関数で遅延させることができ、それぞれが逆の順序で一度だけ実行されます。あなたは閉鎖を延期することもできます - ちょうど括弧を忘れないでください!

defer func(){
    // Do some cleanup
}()


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow