同じ記事を別のカレンダーに登録できないことに気づいて必死にネタを探しました。
うちのソフトウェアで必要に駆られて製作したappinstanceクレートを少し前に切り出して正式にpublishしたのでその紹介です。
つかいかた
appinstanceは主にアプリケーションの実行と同じライフタイムを持つ唯一のオブジェクト(つまりシングルトン)を定義するためのライブラリです。
appinstanceの実態はひとつのマクロなので、利用は次の一行を追加するだけでおしまいです。
AppInstance!(pub static instance: SelfObject = SelfObject::new());
staticとDrop
Rustにはすでにstatic
というキーワードが存在しており、これを使うとアプリケーションと同等のライフタイムを持つデータを定義することが可能になっています。ただしこの定義はコンパイル時の定数を必要としており、HashMap
やその他複雑な初期化を必要とするオブジェクトをstatic変数に持つことは難しい問題でした。
ここで、現在はlazy_staticを用いて解決する方法がとられると思います。これもマクロを提供するライブラリで、名前の通り遅延して評価されるstatic変数を定義するものです。その実体は、ポインタとOnce
のペアの構造体で、初期状態ではポインタに0が格納されており、deref
が呼ばれるとOnce
により初期化処理が一回だけ実行される、というものになっています。
lazy_staticを利用することでおおむねstaticがらみの問題は解決するのですが、lazy_staticにも難点があって、それはDrop::drop
が実行されないという点です。ウィンドウシステムを利用するプログラムなどでRAIIを実装している場合、dropに重要な処理が書かれている場合があるので問題となることがあります。
ここでAppInstanceの出番になります。AppInstanceでは遅延して初期化すると同時にlibc::atexit
にdropの処理を登録するようになっていて、これのおかげで(正常に終了すれば)生成と逆順にdropが呼ばれるようになります。dropの順番も定義されているので順番が重要なオブジェクトの解放も行うことが可能です。
欠点
AppInstanceはimmutableな参照しか返さないので可変である必要があるデータはAppInstanceにできないのですが、static mut
がunsafeなのとCell/RefCellを使えば実現できなくもないのでこの用途のサポートはしないつもりです。
参考
rust - How do I create a global, mutable singleton? - Stack Overflow