【Nim】seq型とstring型のリファレンスの罠【追記:v0.19.0にて解決済み】
[追記]
Nimバージョン 0.19.0 で、seq型とstring型のデフォルト値が nil ではなく @[] と "" になりました。この仕様変更により、ここで指摘していた"罠"は根本的に解決されたことになります。一応この記事は残しておきますが、もう気にする必要がないことです。
Nimバージョン 0.19.0 で、seq型とstring型のデフォルト値が nil ではなく @[] と "" になりました。この仕様変更により、ここで指摘していた"罠"は根本的に解決されたことになります。一応この記事は残しておきますが、もう気にする必要がないことです。
[執筆時のNimバージョン : 0.17.2]
seq型とstring型のリファレンスの罠に引っかかったので、記事に残しておこうと思います。
罠を紹介するために事前知識として「seq型とstring型のデフォルト値」と「リファレンスのデフォルト値」について説明します。
まず、seq型とstring型のデフォルト値についてです。Nimのseq型とstring型はデフォルト値がnilとなっています。そのため、実体を作るまで要素の追加などの操作ができません。seq型やstring型を利用するにはまず実体を作る必要があります。
var s: string s.add( "aaa" ) # 中身がnilなのでエラー s = "" # 実体を用意 s = newString(0) # これでもよい s.add( "aaa" ) # OK
次にリファレンスについてです。リファレンスも同じようにデフォルト値がnilなので、アクセスするにはnewプロシージャでインスタンスを作る必要があります。
var i: ref int i.new # インスタンス作成 i[] = 10 # []はデリファレンス演算子
問題なのはseq型やstring型のリファレンス(ref)を利用する場合です。newプロシージャでseq型やstring型のインスタンスを作っても、 まだその中身はnilなのです。なぜなら先ほど説明したように、seq型やstring型のデフォルト値がnilだからです。
{.experimental.} # 自動デリファレンスに必要 var s: ref string s.new # string型インスタンスを作成 s.add( "aaa" ) # エラー!インスタンスを作ったのになぜ!
なので、seq型やstring型のリファレンスを扱う場合は、まずseq型やstring型自体のインスタンスを作り、さらにその中身を作成する必要があります。
{.experimental.} # 自動デリファレンスに必要 var s: ref seq[int] s.new # seq型インスタンスを作成 s.newseq( 0 ) # seq型インスタンスの内部的なインスタンスを作成 s.add( 1 ) # アクセス可能
これがseq型とstring型のリファレンスの利用する際の罠です。seq型とstring型のデフォルト値がnilなことを考えれば、そのリファレンスを扱う場合に2回インスタンスを作成する必要があるのは当然のことなのですが、結構はまりやすい罠だと思うので紹介しました。