【Rust】Sized マーカートレイト


sized 型

Rust のほとんどの型は、値のメモリ上におけるサイズが既知であり、このような型を sized型 と呼ぶ。

u64は 8 バイトであり、(f32, f32, f32)型のタプルは 12 バイトであり、sized型 である。 さらに、列挙型や Vec<T> 値でさえも sized型 である。

列挙型は、常に最大のヴァリアントを保持できる空間を要する sized型 であり、Vec<T> 値そのものはバッファへのポインタと容量と長さを保持する sized型 である(ヒープ上に所有されるバッファ自体は可変)。

このように Rust のほとんどの型は sized 型であるが、unsized な型もいくつか存在する。

文字列スライス型 str や、[T] のような配列スライス型(& が付いていないことに注意)は、さまざまなサイズの値の集合を指すため unsized 型であり、トレイトオブジェクトとして参照される dyn 型も unsized 型である。

最後のフィールドが Sized でない struct も unsized 型である(最後のフィールドだけを unsized 型とすることができる)。


Rustでは、unsized 型の値は、変数に格納したり、引数として渡すことはできない。そのため、&strBox<dyn Write> のようにポインタを介して扱わなければならない(ポインタはサイズが決まっている)。


std::marker::Sized トレイト

sized 型 は、暗黙裡に std::marker::Sized トレイトを実装する(Rust コンパイラが、適用できるすべての型に自動的に実装し、自身で実装することはできない)。

std::marker::Sized トレイト自体は空実装であり、マーカートレイトとなっている。

pub trait Sized {
    // Empty.
}


ほとんどの型は sized 型であるため、型変数は sized 型 が暗黙のデフォルトとなっている。

struct S<T> { ... } と書けば、struct S<T: Sized> { ... } と解釈される。

トレイト境界として Sized の制約を課したくない場合は、Dynamically Sized Types として ?Sized を明示的に指定する必要がある。

struct S<T: ?Sized> {
   ...
}

?Sized は、Sized かもしれないし、そうでないかもしれないということを意味する。

以下のように定義すれば、

struct S<T: ?Sized> {
    b: Box<T>
}

S<i32> のように書くこともできるし、S<str>S<dyn Write> のように書くこともできる。

S<i32> とした場合は sized型 となり、Box は通常のポインタ(8byte)となるが、S<str>S<dyn Write> とした場合はファットポインタ(16byte)となる。つまり、ポインタアドレスに加え、可変領域の長さの情報が付与されることになる。