deferとは?
deferキーワードは、関数の終了時に実行される関数呼び出しを指定するために使用されます。deferを使うことで、リソースの解放やクリーンアップ処理を簡潔に記述することができます。
基本的な使い方
以下は、deferの基本的な使い方の例です。
package main
import "fmt"
func main() {
fmt.Println("start")
defer fmt.Println("deferred")
fmt.Println("end")
}
このプログラムの出力は以下のようになります。
start
end
deferred
deferを使った関数呼び出しは、main関数の終了時に実行されるため、endの後にdeferredが出力されます。
deferの利点
1. リソースの解放
deferは、ファイルやデータベース接続などのリソースを確実に解放するために使用されます。以下は、ファイルを開いて読み取る例です。
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open("example.txt")
if err != nil {
fmt.Println(err)
return
}
defer file.Close() // 関数終了時にファイルを閉じる
// ファイルの読み取り処理
// ...
}
この例では、defer file.Close()によって、関数が終了する際に必ずファイルが閉じられることが保証されます。
2. 複数のdefer
複数のdeferを使用することも可能です。複数のdeferは、LIFO(Last In, First Out)順に実行されます。
package main
import "fmt"
func main() {
defer fmt.Println("first")
defer fmt.Println("second")
defer fmt.Println("third")
}
このプログラムの出力は以下のようになります。
third
second
first
最後に指定されたdeferが最初に実行されるため、逆順に出力されます。
3. エラーハンドリング
deferは、エラーハンドリングと組み合わせて使用することもできます。以下は、パニックが発生した場合にリカバリする例です。
package main
import "fmt"
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from:", r)
}
}()
fmt.Println("Starting the program")
panic("Something went wrong!")
fmt.Println("This line will not be executed")
}
このプログラムの出力は以下のようになります。
Starting the program
Recovered from: Something went wrong!
deferを使ってリカバリ関数を指定することで、パニックが発生してもプログラムがクラッシュせずに処理を続行できます。
deferの注意点
1. 遅延実行のコスト
deferは便利ですが、頻繁に使用するとパフォーマンスに影響を与えることがあります。特に、ループ内で多用する場合は注意が必要です。
2. 遅延実行のタイミング
deferは関数の終了時に実行されるため、関数の途中でリソースを解放したい場合には適していません。その場合は、明示的にリソースを解放する必要があります。
まとめ
deferは、Go言語における強力な機能の一つであり、リソースの解放やクリーンアップ処理を簡潔に記述するために非常に便利です。deferを適切に活用することで、コードの可読性と保守性を向上させることができます。