팬텀 타입은 사용자 정의 타입의 타입 매개변수 중에서, 그 타입의 값 생성자 어디에서도 사용되지 않는 것을 말해요.
조금 추상적이니 예시를 하나 볼까요?
pub type Length(unit) {
Length(amount: Float)
}
이 예시에서 unit 타입 매개변수는 Length 값 생성자에서 사용되지 않아요. 그래서 unit은 팬텀 타입이에요.
이렇게 사용되지 않는 타입 매개변수는 쓸모없어 보일 수 있지만, Length 값을 어떻게 쓸 수 있는지에 제약을 더하는 데 활용할 수 있어요.
예를 들어 길이를 두 배로 만드는 double 함수를 생각해봐요. 이 함수는 어떤 단위의 길이와도 동작하기 때문에, 타입 매개변수가 제네릭 타입 변수가 돼요.
// This function accepts all Length values
pub fn double(length: Length(unit)) -> Length(unit) {
Length(length.amount *. 2.0)
}
길이가 인치일 때만 동작하는 add_inch 함수도 만들 수 있어요.
// A unit type for inches. It is never constructed so we don't
// define any constructors for it.
pub type Inches
pub fn add_inch(length: Length(Inches)) -> Length(Inches) {
Length(length.amount +. 1.0)
}
add_inch 함수는 다른 단위 매개변수를 가진 길이는 받지 않아요. 팬텀 타입을 이용해 올바른 단위만 쓰이도록 한 거예요.
같은 타입 변수를 둘 다에 사용해서, 두 길이 값이 같은 단위임을 보장하는 함수를 작성할 수도 있어요.
pub fn add(a: Length(unit), b: Length(unit)) -> Length(unit) {
Length(a.amount +. b.amount)
}
let two_meters: Length(Meters) = Length(2.0)
let two_inches: Length(Inches) = Length(2.0)
add(two_meters, two_meters)
// -> Length(4.0): Length(Meters)
add(two_meters, two_inches)
// Type error! The unit type parameters do not match.
팬텀 타입은 불투명 타입과 잘 어울려요. 다른 모듈에서 Length 값을 만들 수 없다면, 잘못된 단위 타입으로 값이 만들어지지 않도록 보장할 수 있고, 위에서 정의한 함수들만 그 값에 쓸 수 있게 할 수 있어요.