💎

【Crystalと○○】Crystalと参照型/値型

2021/07/10に公開

📖【Crystalと○○】コンテンツ一覧

Crystal には普段あまりお目にかかることのない Reference 型と Value 型という2つの特殊な型が定義されています。これらは Object 型を直接継承しており、またこれら以外の型が Object 型を直接継承することはありません。

つまり、Crystal における継承ツリーは、Object 型を基底としてまず Reference 型と Value 型に枝分かれし、それぞれからさらにツリーが枝分かれするような構造となっています。

  • Object
    • Reference
      • ...
    • Value
      • ...

このようなツリー構造になっているのは、Crystal の型が大きく参照型と値型の2種類に別れることが理由です。

参照型(reference type)

参照型はインスタンス化の際にヒープ上にメモリを割り当てます。また、参照型のインスタンスをメソッドの引数や返り値として渡したり変数への代入したりする際に、実際に渡されるのはそのインスタンスのポインタになります(参照渡し)。

Crystal では全てのクラス(class)が参照型であり、Reference 型はその参照型の基底となる型です。明示的に継承元を指定されていない全てのクラスは、暗黙的にこの Reference 型を親クラスとして定義されることになります。

class SomeClass # < Reference
end

Reference 型には、Object 型で抽象定義(abstract def)されたままになっているメソッドが一式実装されていますので、上記のような記述でも Crystal オブジェクトとして最低限必要な機能が提供されます。

値型(value type)

値型はインスタンス化の際にスタック上にメモリを確保します。また、値型のインスタンスをメソッドの引数や返り値として渡したり変数への代入したりする際に、実際に渡されるのはインスタンス自身ではなくその浅いコピー(shallow copy)になります(値渡し)

Crystal の構造体(struct)は全て値形で、Value 型はその基底となる型です。

標準ライブラリで提供される構造体の一部(NilBoolCharNumberSymbolPointerTupleStaticArrayなど)はValue 型を直接継承しており、それ以外の構造体は Value 型を継承した Struct 型から継承されます。

参照型の場合とは異なり、ユーザが構造体を定義する際の暗黙的な親クラスは Value 型ではなく Struct 型です。

struct SomeStruct # < Struct
end

そのため、Value 型で実装されている Object 型の抽象メソッドは、「何と比較しても必ず false を返す」#== メソッドと、「自身の浅いコピーを返す」#dup メソッドくらいのもので、残りは Struct 型や、その他 Value 型を継承したそれぞれの型で実装されています。

通常の利用では想定されませんが、もし万が一、何らかの理由(およそ思いつきませんが)で Value を直接継承する構造体を定義したいような場合、#==#dup 以外の Object 型の抽象メソッドを全て実装する必要があります。

今回のまとめ

  • Crystalには参照型の基底となる Reference 形と、値型の規定となる Value 形が存在する
  • 参照型のインスタンスはヒープ領域にメモリを割り当てられ、参照渡しされる
  • 値型のインスタンスはスタック領域にメモリを割り当てられ、値渡しされる

Discussion