Rust にはこのAny
特性がありますが、「使用しないものにはお金を払わない」というポリシーもあります。Rust はリフレクションをどのように実装するのでしょうか?
私の推測では、Rust は遅延タグ付けを使用します。すべての型は最初は割り当てられていませんが、後でその型のインスタンスが特性を期待する関数に渡されるとAny
、その型には が割り当てられますTypeId
。
あるいは、Rust は、そのインスタンスがその関数に渡される可能性があるすべての型に を配置するのでしょうかTypeId
? 前者はコストがかかると思われます。
ベストアンサー1
まず、Rustにはリフレクションがありません。リフレクションとは、実行時にフィールド、メソッド、実装するインターフェースなど、型の詳細を取得できることを意味します。等あなたできないこれを Rust で実行します。最も近似できるのは、この情報を提供する特性を明示的に実装 (または派生) することです。
各型にはTypeId
コンパイル時にIDが割り当てられます。グローバルに順序付けられたIDを持つことは難しいIDは、型の定義と、それが含まれるクレートに関するさまざまなメタデータの組み合わせから得られる整数です。言い換えると、それらは何らかの順序で割り当てられるわけではなく、単にハッシュ型を定義する際に使用されるさまざまな情報のビット。[1]
を見てみるとAny
特性の源、次の単一の実装が表示されますAny
:
impl<T: 'static + ?Sized > Any for T {
fn get_type_id(&self) -> TypeId { TypeId::of::<T>() }
}
(境界は非公式に「他のものから借用されていないすべての型」に縮小されます。
の定義もご覧いただけますTypeId
:
pub struct TypeId {
t: u64,
}
impl TypeId {
pub const fn of<T: ?Sized + 'static>() -> TypeId {
TypeId {
t: unsafe { intrinsics::type_id::<T>() },
}
}
}
intrinsics::type_id
はコンパイラによって認識される内部関数であり、型が与えられるとその内部型IDを返します。この呼び出しはコンパイル時にリテラル整数型IDに置き換えられるだけで、実際のここで呼び出します。[2] は、TypeId
型のIDが何であるかを知る方法です。TypeId
は、実装の詳細をユーザーから隠すためのラッパーです。 概念的にもっとシンプルにしたい場合は、型のを、コンパイラが単に64ビットの定数整数としてu64
考えることができます。TypeId
知っているコンパイル時に。
Any
からこちらに転送されますget_type_id
。つまりget_type_id
、本当に特性メソッドを適切なTypeId::of
メソッドにバインドするだけです。これは、 がある場合にAny
元の型の を見つけることができるようにするためだけのものですTypeId
。
現在、Any
実装されているほとんど種類はありますが、これはすべての種類が実際に持っているメモリ上に浮遊する実装。実際には、コンパイラは、次の場合にのみ型の実装Any
の実際のコードを生成します。Any
誰かそれを必要とするコードを記述します。[3] 言い換えれば、Any
特定の型の実装を一度も使用しない場合は、コンパイラはそれを生成しません。
これは、Rust が「使用しないものにはお金を払わない」を実現する方法です。つまり、特定の型を&Any
またはとして渡さない場合Box<Any>
、関連するコードは生成されず、コンパイルされたバイナリ内でスペースを占有することはありません。
[1]: 残念なことに、これは、型TypeId
の値を変更する正確にはどうやってライブラリはコンパイルされ、依存関係としてコンパイルすると (スタンドアロン ビルドではなく) TypeId
s が変更されます。
[2]: 私が知る限りでは、私はできたこれについては間違っているかもしれないが、私は本当にもしそうだとしたら驚きです。
[3]: これは一般的にRust のジェネリックにも当てはまります。