Flat Leon Works

アプリやゲームを作ってます。

【Nim】seq型とstring型のリファレンスの罠【追記:v0.19.0にて解決済み】

[追記]
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回インスタンスを作成する必要があるのは当然のことなのですが、結構はまりやすい罠だと思うので紹介しました。