golangはstructにデストラクタ的なものを定義することは出来ないが、インスタンスがGCに拾われる時に任意の処理をさせることはできる。
具体的方法
runtimeパッケージのSetFinalizer()でfinalizer関数を設定できる。いわゆるデストラクタのようにstructの宣言に付けられるわけではなくて、インスタンスごとに設定しなければならない。
# なのでデストラクタではない
- runtime#SetFinalizer
createInstance()でfinalizer関数を設定している。runtime.SetFinalizerには対象のインスタンスの参照とfinalizerとして設定する関数を渡す。
func finalizer(m *string) {
fmt.Println("finalize for ->", *m)
return
}
func createInstance() {
x := "hello"
runtime.SetFinalizer(&x, finalizer)
return
}
func main() {
createInstance()
runtime.GC()
time.Sleep(1 * time.Second)
return
}
finalize for -> hello
finalizer関数は新しく立ち上げられたgoroutineで実行される。(このgoroutineが実行される時間をsleepで稼いでる)
finalizer()で出力させている文字列が出ていて、GCで回収されたタイミングで実行されていることが判る。
注意点
- SetFinalizerを使用したインスタンスは、Finalizerが動作したタイミングではGCに回収されない。次回のGCで回収される。
- finalizer関数は必ず実行されるわけではない。つまり、GCに回収されないケース(プロセスが終了する・GCが止まっている)は漏れる。
- deferであれば確実に実行される
用途
あんまり綺麗じゃないし確実でもないので、積極的には使わないほうがいいような気がする。
個人的には、型じゃなくてインスタンスによって重要な動作が変わるのが、ヒジョーに気持ち悪い。
# 美観の問題かもだけど、deferで間に合うように書いたほうが読みやすい
とはいえcgoでc言語の関数をラップするとかの時とか、まぁどうしようもない事が時々ある。そんなときにだけ使いたい。