パラメータの Ty/Const/Region
ジェネリックアイテムの内部では、スコープ内のジェネリックパラメータを使用する型を書くことができます。たとえば fn foo<'a, T>(_: &'a Vec<T>) です。
この具体的なケースでは、&'a Vec<T> 型は内部的には次のように表現されます。
TyKind::Ref(
RegionKind::LateParam(DefId(foo), DefId(foo::'a), "'a"),
TyKind::Adt(Vec, &[TyKind::Param("T", 0)])
)
ジェネリックパラメータの使用は、3 つの別々の方法で表現します。
TyKind::Param/ConstKind::Param/RegionKind::EarlyParamは早期束縛ジェネリックパラメータ用です(注: すべての型パラメータと const パラメータは早期束縛と見なされます。詳細は早期束縛パラメータと後期束縛パラメータの章を参照してください)TyKind::Bound/ConstKind::Bound/RegionKind::Boundは、高ランク境界または高ランク型を介して導入されたパラメータへの参照用です。つまり、for<'a> fn(&'a u32)やfor<'a> T: Trait<'a>です。これについてはBinderの章で説明します。RegionKind::LateParamは後期束縛ライフタイムパラメータ用です。LateParamについてはBinderのインスタンス化の章で説明します。
この章では、TyKind::Param、ConstKind::Param、RegionKind::EarlyParam のみを扱います。
Ty/Const パラメータ
TyKind::Param と ConstKind::Param は同じように実装されているため、このセクションでは簡単のため TyKind::Param のみに言及します。
ただし、ここで述べることはすべて ConstKind::Param にも当てはまることを覚えておいてください。
各 TyKind::Param には、パラメータの名前とインデックスという 2 つのものが含まれます。
TyKind::Param の使用例として、次の具体例を見てください。
struct Foo<T>(Vec<T>);
Vec<T> 型は、TyKind::Adt(Vec, &[GenericArgKind::Type(Param("T", 0))]) として表現されます。
名前はある程度自明で、型パラメータの名前です。 型パラメータのインデックスは、スコープ内にあるジェネリックパラメータのリストにおける その順序を示す整数です。 これには、そのパラメータが定義されているアイテムよりも外側のスコープにあるアイテムで定義されたパラメータも含まれることに注意してください。 次の例を考えてみましょう。
struct Foo<A, B> {
// A のインデックスは 0 になる
// B のインデックスは 1 になる
.. // いくつかのフィールド
}
impl<X, Y> Foo<X, Y> {
fn method<Z>() {
// ここでは、X、Y、Z はすべてスコープ内にある
// X のインデックスは 0
// Y のインデックスは 1
// Z のインデックスは 2
}
}
具体的には、パラメータが定義されているアイテムの ty::Generics が与えられたとき、
インデックスが 2 で、ルートの parent から始める場合、それは導入される 3 番目のパラメータになります。
たとえば上の例では、Z のインデックスは 2 であり、impl ブロックから始めて導入される 3 番目のジェネリックパラメータです。
インデックスによって Ty は完全に定義され、コンパイルしているコードについて推論する上で重要な TyKind::Param の唯一の部分です。
通常、名前が何であるかは気にせず、インデックスのみを使用します。
名前は診断とデバッグログのために含まれています。そうしなければ、
出力を理解することが非常に難しくなるからです。つまり、Vec<Param(0)>: Sized と Vec<T>: Sized の違いです。デバッグ出力では、パラメータ型は
しばしば {name}/#{index} として出力されます。たとえば関数 foo で Vec<T> をデバッグ出力すると、Vec<T/#0> と書かれます。
代替表現として、名前だけを持つことも考えられます。
しかし、インデックスを使用する方が効率的です。いくつかの引数でジェネリックパラメータをインスタンス化するときに GenericArgs にインデックスアクセスできることを意味するからです。
そうしない場合、GenericArgs を HashMap<Symbol, GenericArg> として保存し、ジェネリックアイテムを使用するたびにハッシュマップ検索を行う必要があります。
理論上は、インデックスを使うことで、同じ名前を使用する複数の別々のパラメータを持つことも可能になります。たとえば、
impl<A> Foo<A> { fn bar<A>() { .. } } です。
シャドーイングを禁止するルールによってこれは難しくなっていますが、そうした言語ルールは将来変わる可能性があります。
ライフタイムパラメータ
Ty/Const の単一の Param バリアントとは対照的に、ライフタイムにはリージョンパラメータを表現するための 2 つのバリアントがあります。RegionKind::EarlyParam と RegionKind::LateParam です。
その理由は、関数が早期束縛パラメータと後期束縛パラメータを区別するためであり、これについては前の章で説明されています(リンクを参照)。
RegionKind::EarlyParam は、Ty/Const の Param バリアントと同じ構造です。単に u32 インデックスと Symbol です。
非関数アイテムで定義されたライフタイムパラメータには、常に ReEarlyParam を使用します。
関数では、早期束縛パラメータには ReEarlyParam を使用し、後期束縛パラメータには ReLateParam を使用します。
Ty および Const パラメータと同様に、これらはデバッグフォーマットでしばしば 'SYMBOL/#INDEX として出力されることに注意してください。
例:
// この関数のシグネチャは次のように表現される:
//
// ```
// fn(
// T/#2,
// Ref('a/#0, Ref(ReLateParam(...), u32))
// ) -> Ref(ReLateParam(...), u32)
// ```
fn foo<'a, 'b, T: 'a>(one: T, two: &'a &'b u32) -> &'b u32 {
...
}
RegionKind::LateParam については、バインダーのインスタンス化の章でさらに説明します。