기저 타입
기저 타입 (Underlying types)
Go에서 두 타입이 서로 '같은 타입'인지, 아니면 서로 호환되는 타입인지를 판단할 때 기준이 되는 개념이 있어요. 바로 underlying type이에요. 이 개념을 이해하면 string과 A1이 왜 같은 타입 취급을 받는지, B1과 B2는 또 왜 그런지가 자연스럽게 보여요. 예시를 보면서 하나씩 따라가 볼게요.
출처: Go Specification
본문
모든 타입 T는 각자 자신만의 underlying type을 하나씩 갖고 있어요. 규칙은 의외로 단순해요. T가 미리 선언된(predeclared) boolean·numeric·string 타입 중 하나이거나 타입 리터럴(type literal)이라면, T의 underlying type은 바로 T 그 자체가 돼요. 그 외의 경우에는, T가 자기 선언에서 참조하는 타입의 underlying type이 곧 T의 underlying type이 되죠. 타입 파라미터(type parameter)의 경우에는 조금 달라요. 타입 파라미터의 underlying type은 자기 type constraint의 underlying type인데, 그 type constraint는 언제나 인터페이스(interface)예요.
type (
A1 = string
A2 = A1
)
type (
B1 string
B2 B1
B3 []B1
B4 B3
)
func f[P any](x P) { … }
코드 위쪽의 A1, A2는 string을 그대로 가리키는 별칭(alias)이에요. 그래서 string, A1, A2는 물론, 아래쪽에서 string으로 새로 정의한 B1과 B1을 가리키는 B2까지, 이 다섯 타입의 underlying type이 전부 string이 돼요. 한편 []B1은 리터럴이고, B3는 []B1을, B4는 B3를 가리키죠. 그래서 []B1, B3, B4의 underlying type은 모두 []B1이에요. 마지막으로 제네릭 함수의 타입 파라미터 P는 any로 제약을 걸었는데, any는 interface{}의 별칭이니 P의 underlying type은 interface{}가 돼요.
이렇게 정리하면, underlying type이 같다는 것이 곧 타입이 서로 호환 가능한지를 가늠하는 기준이 되어요. 이름이 서로 다른 타입처럼 보여도 underlying type이 같으면 서로 연결되는 타입들이라는 뜻이죠.
더 알아보기
- Type identity — 두 타입이 '동일(identical)'한지, '다르다(different)'고 볼지는 어떻게 판단하는지 이어서 다뤄요.
- Type definitions — 별칭(alias)과 새 타입 정의(new defined type)의 차이가 여기서 빛을 발해요.
- Type constraints — 타입 파라미터의 underlying type이 결정되는 근거가 되는 제약 조건이에요.