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

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)])
)

제네릭 매개변수의 사용을 표현하는 방법은 세 가지가 있습니다.

이 장에서는 TyKind::Param, ConstKind::Param, RegionKind::EarlyParam만 다룹니다.

타입/상수 매개변수

TyKind::Param과 ConstKind::Param은 동일하게 구현되어 있으므로, 이 절에서는 편의상 TyKind::Param만 언급합니다. 다만 여기서 설명하는 모든 내용이 ConstKind::Param에도 똑같이 적용된다는 점을 기억하십시오.

각 TyKind::Param에는 매개변수의 이름과 인덱스라는 두 가지 정보가 들어 있습니다.

다음은 TyKind::Param을 사용하는 구체적인 예시입니다.

struct Foo<T>(Vec<T>);

Vec<T> 타입은 TyKind::Adt(Vec, &[GenericArgKind::Type(Param("T", 0))])로 표현됩니다.

이름은 말 그대로 타입 매개변수의 이름입니다. 타입 매개변수의 인덱스는 스코프 안의 제네릭 매개변수 목록에서 해당 매개변수의 순서를 나타내는 정수입니다. 여기에는 매개변수가 정의된 아이템보다 바깥 스코프에 있는 아이템에서 정의된 매개변수도 포함된다는 점에 유의하십시오. 다음 예시를 살펴보십시오.

struct Foo<A, B> {
  // A would have index 0
  // B would have index 1

  .. // some fields
}
impl<X, Y> Foo<X, Y> {
  fn method<Z>() {
    // inside here, X, Y and Z are all in scope
    // X has index 0
    // Y has index 1
    // Z has index 2
  }
}

구체적으로 말하면, 매개변수가 정의된 아이템의 ty::Generics가 주어졌을 때 인덱스가 2라면, 최상위 parent부터 시작하여 세 번째로 도입되는 매개변수가 됩니다. 예를 들어 위 예시에서 Z는 인덱스가 2이며, impl 블록부터 시작하여 세 번째로 도입되는 제네릭 매개변수입니다.

인덱스는 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에 해당하는 단일 Param 변형과 달리, 라이프타임에는 리전 매개변수를 표현하기 위한 변형이 두 가지 있습니다. RegionKind::EarlyParam과 RegionKind::LateParam입니다. 그 이유는 함수가 조기 바인딩 매개변수와 후기 바인딩 매개변수를 구분하기 때문이며, 이는 앞선 장에서 다루었습니다(링크 참조).

RegionKind::EarlyParam은 Ty/Const의 Param 변형과 구조가 동일하며, 단순히 u32 인덱스와 Symbol로 이루어져 있습니다. 함수 항목이 아닌 항목에 정의된 라이프타임 매개변수에는 항상 ReEarlyParam을 사용합니다. 함수의 경우 조기 바인딩 매개변수에는 ReEarlyParam을, 후기 바인딩 매개변수에는 ReLateParam을 사용합니다. Ty 및 Const 매개변수와 마찬가지로, 이들도 흔히 'SYMBOL/#INDEX 형식으로 디버그 출력한다는 점에 유의하십시오.

예시는 다음과 같습니다.

// This function would have its signature represented as:
//
// ```
// 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은 바인더 인스턴스화에 관한 장에서 더 자세히 다룹니다.