Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

메서드 조회

메서드 조회는 self 타입, 자동 역참조(autoderef), 트레이트 조회 등 여러 요인이 상호 작용하기 때문에 상당히 복잡할 수 있습니다. 이 파일은 그 과정의 개요를 제공합니다. 더 자세한 설명은 당연히 코드 자체에 있습니다.

메서드 조회를 이해하는 한 가지 방법은 receiver.method(...) 형태의 표현식을 더 명시적인 완전 한정 구문(이전에는 UFCS라고 불렸습니다)으로 변환한다고 생각하는 것입니다:

  • 트레이트 호출의 경우 Trait::method(ADJ(receiver), ...)
  • 고유 메서드 호출의 경우 ReceiverType::method(ADJ(receiver), ...)

여기서 ADJ는 일종의 조정으로, 일반적으로 일련의 자동 역참조 뒤에 경우에 따라 자동 참조(autoref)가 이어지는 형태입니다(예: &**receiver). 그러나 때로는 그 과정에서 다른 조정과 강제 변환도 수행하며, 특히 크기 제거(unsizing)가 그러합니다(예: [T; n]에서 [T]로 변환).

메서드 조회는 두 가지 주요 단계로 나뉩니다:

  1. 탐색(probe.rs). 프로브 단계는 어떤 메서드를 호출할지, 그리고 수신자를 어떻게 조정할지 결정하는 단계입니다.
  2. 확정(confirm.rs). 확정 단계는 이 선택을 “적용“하여 부가 테이블(side-table)을 갱신하고, 타입 변수를 단일화하며, 그 밖의 부수 효과가 있는 작업을 수행합니다.

이렇게 나누는 이유 중 하나는 캐싱에 더 적합하게 만들기 위해서입니다. 프로브 단계는 “pick”(probe::Pick)을 생성하며, 이는 여러 메서드 호출 지점에 걸쳐 캐시할 수 있도록 설계되었습니다. 따라서 여기에는 추론 변수나 기타 정보가 포함되지 않습니다.

프로브 단계

스텝

프로브 단계가 가장 먼저 하는 일은 일련의 스텝을 만드는 것입니다. 이는 리시버 타입을 더 이상 역참조할 수 없을 때까지 점진적으로 역참조하고, 선택적으로 “크기 제거(unsize)” 스텝을 적용하는 방식으로 이루어집니다. 따라서 리시버의 타입이 Rc<Box<[T; 3]>>라면 다음과 같은 결과가 나올 수 있습니다:

  1. Rc<Box<[T; 3]>>
  2. Box<[T; 3]>
  3. [T; 3]
  4. [T]

후보 수집

그런 다음 이 스텝들을 따라 탐색하여 후보 목록을 만듭니다. Candidate는 호출되는 메서드일 가능성이 있는 메서드 항목입니다. 각 후보에 대해 명시적 self를 고려한 “변환된 self 타입“을 도출합니다.

후보는 고유(inherent) 후보와 확장(extension) 후보의 두 종류로 분류됩니다.

고유 후보(inherent candidates)는 리시버 타입 자체에서 도출되는 후보입니다. 따라서 어떤 명목 타입 Foo(예: 구조체)의 리시버가 있다면, impl Foo와 같은 impl 안에 정의된 모든 메서드는 고유 메서드입니다. 고유 메서드를 사용하는 데에는 아무것도 임포트할 필요가 없습니다. 고유 메서드는 타입 자체에 연관되어 있기 때문입니다. 고유 impl은 해당 타입과 같은 크레이트에서만 정의할 수 있다는 점에 유의하십시오.

확장 후보(extension candidates)는 임포트된 트레이트에서 도출됩니다. 트레이트 ToString을 임포트한 상태에서 to_string()을 메서드로 호출하면, ToString의 각 impl에 있는 to_string() 정의를 후보로 나열합니다. 이러한 종류의 메서드 호출을 “확장 메서드(extension methods)“라고 합니다.

그러면 예제를 계속 살펴봅시다. 리시버 Rc<Box<[T; 3]>>로 메서드 foo를 호출하고 있고, 타입 Rc<U>에 대해 &self로 이 메서드를 정의하는 트레이트 Foo가 있으며, 타입 Box에도 foo를 정의하되 &mut self로 정의하는 메서드가 있다고 가정해 보십시오. 그러면 다음과 같은 두 후보가 생길 수 있습니다.

  • 확장 후보로서의 &Rc<U>
  • 고유 후보로서의 &mut Box<U>

후보 탐색

마지막으로, 실제로 메서드를 선택하기 위해 단계들을 차례로 따라 내려가며 리시버 타입을 후보 타입들과 매칭해 봅니다. 각 단계에서는 자동 참조(auto-ref)와 자동 가변 참조(auto-mut-ref)도 고려하여, 그렇게 했을 때 후보 중 하나라도 매칭되는지 확인합니다. 이렇게 얻은 각 리시버 타입에 대해 확장 후보보다 고유 후보를 먼저 고려합니다. 한 그룹 안에 매칭되는 후보가 여러 개 있으면 오류를 보고합니다. 단, 같은 트레이트의 여러 impl은 하나의 매칭으로 취급합니다. 그렇지 않으면 처음 찾은 매칭을 선택합니다.

예제의 경우 첫 번째 단계는 Rc<Box<[T; 3]>>이며, 이 타입 자체는 어떤 후보와도 매칭되지 않습니다. 하지만 이를 자동 참조하면 &Rc<U>와 매칭되는 타입 &Rc<Box<[T; 3]>>를 얻습니다. 그런 다음 해당 impl에 나타나는 모든 where 절을 재귀적으로 고려합니다. 그 절들이 매칭되면(또는 매칭되지 않는다고 배제할 수 없으면) 이 메서드를 선택하게 됩니다. 그렇지 않으면 일련의 단계를 계속 따라 내려갑니다.