この記事は「Nim Advent Calendar 2021」の23日目の記事として登録させてもらっています。
弱参照とは
弱参照の定義はいろいろあると思いますが、ここでは参照先が破棄されたことを検知できる参照という意味で扱います。Nimは言語としては弱参照は用意されていません。Nimの参照は、参照している限りインスタンスは破棄されないので、破棄されたことを検知することはできません。
弱参照を実装してみる
弱参照は、参照情報を共有することで実現できます。インスタンスが破棄されたときに参照情報が持つ参照先をnilにすることで、同じ参照先を指す弱参照すべてでインスタンスが破棄されたということを検知することができます。
type WeakRef[T] = ref object p: pointer proc get*[T]( self: WeakRef[T] ): T = return cast[T]( self.p ) type UniqueRef[T] = object uniqueRef*: WeakRef[T] proc init*[T](self: var UniqueRef[T], p: var T) = self.uniqueRef = WeakRef[T]() self.uniqueRef.p = p[].addr proc `=destroy`[T]( self: var UniqueRef[T] ) = self.uniqueRef.p = nil proc getWeakRef*[T]( self: var UniqueRef[T] ): WeakRef[T] = self.uniqueRef
WeakRefが弱参照です。WeakRefはUniqueRefから得る必要があります。そして、UniqueRefが破棄されるとWeakRefの参照先がnilになります。
使用例
type MyClass = ref object name*: string var weakRef: WeakRef[MyClass] block: var myClass = MyClass(name:"hoge") var uniqueRef = UniqueRef[MyClass]() # UniqueRefを作成 uniqueRef.init( myClass ) # UniqueRefを初期化 weakRef = uniqueRef.getWeakRef() # WeakRefを取得 assert weakRef.get() != nil # WeakRefの参照先がnilでないことを確認 echo weakRef.get().name assert weakRef.get() == nil # UniqueRefが破棄されたのでWeakRefの参照先がnilになっていることを確認
まず、UniqueRefを作成し、そこからWeakRefを取得します。WeakRefのblockを抜けてUniqueRefが破棄されたあとは、WeakRefの参照先がnilになっています。
注意点
- 参照先の型Tは、参照型である必要があります。
- 全然テストしていないので、何か問題があるかもしれません。
- Nimでデストラクタを機能させるには、ビルドオプションで
--gc:arc
にする必要があるかもしれません。