メソッド検索
メソッド検索は、self 型、自動参照外し、トレイト検索など、多数の要因が相互作用するため、かなり複雑になることがあります。このファイルでは、そのプロセスの概要を説明します。より詳細なメモは、当然ながらコード自体にあります。
メソッド検索の考え方の 1 つは、receiver.method(...) という形式の式を、より明示的な完全修飾構文(以前は UFCS と呼ばれていました)に変換するというものです。
- トレイト呼び出しの場合は
Trait::method(ADJ(receiver), ...) - 固有メソッド呼び出しの場合は
ReceiverType::method(ADJ(receiver), ...)
ここで ADJ は何らかの調整であり、通常は一連の自動参照外しの後に、必要に応じて自動参照が続くものです(例: &**receiver)。ただし、途中で他の調整や型強制を行うこともあり、特にサイズ変更があります(例: [T; n] から [T] への変換)。
メソッド検索は、大きく 2 つのフェーズに分かれます。
- プロービング(
probe.rs)。プローブフェーズでは、どのメソッドを呼び出すか、およびレシーバーをどのように調整するかを決定します。 - 確認(
confirm.rs)。確認フェーズは、この選択を「適用」し、サイドテーブルを更新し、型変数を単一化し、その他の副作用を伴う処理を行います。
この分割の理由の 1 つは、キャッシュにより適したものにするためです。プローブフェーズは「選択結果」(probe::Pick)を生成します。これはメソッド呼び出し箇所間でキャッシュ可能になるように設計されています。そのため、推論変数やその他の情報は含まれません。
プローブフェーズ
ステップ
プローブフェーズが最初に行うことは、一連のステップを作成することです。これは、レシーバー型をそれ以上参照外しできなくなるまで段階的に参照外しし、さらに任意の「サイズ変更」ステップを適用することで行われます。したがって、レシーバーの型が Rc<Box<[T; 3]>> の場合、次のようになる可能性があります。
Rc<Box<[T; 3]>>Box<[T; 3]>[T; 3][T]
候補の組み立て
次に、それらのステップに沿って検索し、候補のリストを作成します。Candidate は、呼び出されているメソッドである可能性が十分にあるメソッド項目です。各候補について、明示的な self を考慮した「変換後の self 型」を導出します。
候補は、固有と拡張の 2 種類に分類されます。
固有候補は、レシーバー自体の型から導出されるものです。つまり、何らかの名目型 Foo(例: 構造体)のレシーバーがある場合、impl Foo のような impl 内で定義されたメソッドはすべて固有メソッドです。固有メソッドを使用するために何かをインポートする必要はありません。それらは型自体に関連付けられています(なお、固有 impl は型自体と同じクレート内でしか定義できません)。
拡張候補は、インポートされたトレイトから導出されます。トレイト ToString をインポートしていて、to_string() をメソッドとして呼び出した場合、ToString の各 impl にある to_string() 定義を候補として列挙します。この種のメソッド呼び出しは「拡張メソッド」と呼ばれます。
では、例を続けましょう。レシーバー Rc<Box<[T; 3]>> でメソッド foo を呼び出しており、それを型 Rc<U> に対して &self で定義するトレイト Foo があり、さらに foo を定義する型 Box 上のメソッドがあるものの、それは &mut self であるとします。この場合、2 つの候補が存在する可能性があります。
- 拡張候補としての
&Rc<U> - 固有候補としての
&mut Box<U>
候補検索
最後に、実際にメソッドを選択するために、ステップを順に下りながら検索し、レシーバー型を候補型と照合しようとします。各ステップでは、自動参照と自動可変参照も考慮し、それによって候補のいずれかが一致するかどうかを確認します。結果として得られる各レシーバー型について、拡張候補より先に固有候補を考慮します。グループ内に一致する候補が複数ある場合はエラーを報告します。ただし、同じトレイトの複数の impl は単一の一致として扱われます。それ以外の場合は、最初に見つかった一致を選択します。
この例の場合、最初のステップは Rc<Box<[T; 3]>> であり、それ自体はどの候補にも一致しません。しかし、自動参照すると、&Rc<Box<[T; 3]>> という型が得られ、これは &Rc<U> に一致します。その後、impl に現れるすべての where 句を再帰的に考慮します。それらが一致する場合(または一致しないと断定できない場合)、これが選択するメソッドになります。そうでなければ、一連のステップをさらに進み続けます。