셀렉터 식
셀렉터 식 (Selectors)
구조체나 인터페이스 값에 점(.)을 찍어서 필드나 메서드에 접근해 본 적이 있죠? x.f처럼 생긴 이 표현을 우리는 selector expression(셀렉터 식)이라고 불러요. 선언할 필요 없이 값의 일부를 바로 끄집어내는, Go에서 가장 자주 만나는 표현 중 하나예요. 이번에는 이 표현이 정확히 무엇을 가리키는지, 그리고 어떤 규칙 아래서 동작하는지 하나씩 풀어볼게요.
출처: Go Specification
본문
package name(패키지 이름)이 아닌 primary expression(일차 식) x에 대해, 셀렉터 식
x.f
은 값 x의 필드 또는 메서드 f를 가리켜요(어쩔 때는 *x를 가리키기도 하는데, 그건 아래에서 볼게요). 여기서 식별자 f를 (필드 또는 메서드) selector(셀렉터)라고 불러요. 이 f는 blank identifier(빈 식별자)가 되면 안 돼요. 셀렉터 식의 타입은 f의 타입과 같고, 만약 x가 패키지 이름이라면 qualified identifiers(한정 식별자) 섹션을 봐야 해요.
셀렉터 f는 타입 T의 필드나 메서드 f를 가리킬 수도 있고, T에 중첩된 embedded field(임베디드 필드)의 필드나 메서드 f를 가리킬 수도 있어요. f에 도달하기 위해 거쳐 간 임베디드 필드의 수를 T에서의 depth(깊이)라고 해요. T 자체에 선언된 필드나 메서드 f의 depth는 0이고, T의 임베디드 필드 A에 선언된 필드나 메서드 f의 depth는 A에서의 f depth에 1을 더한 값이에요.
셀렉터에는 다음 규칙들이 적용돼요:
-
T가 포인터나 인터페이스 타입이 아닐 때, 타입이T또는*T인 값x에 대해x.f는T에서 그런f가 존재하는 가장 얕은 깊이(shallowest depth)의 필드나 메서드를 가리켜요. 만약 가장 얕은 깊이에f가 정확히 하나가 아니라면, 셀렉터 식은 illegal(불법)이 돼요. -
I가 인터페이스 타입일 때, 타입이I인 값x에 대해x.f는x의 동적 값(dynamic value)에서 이름이f인 실제 메서드를 가리켜요.I의 method set(메서드 집합)에 이름이f인 메서드가 없다면 셀렉터 식은 illegal이 돼요. -
예외적으로,
x의 타입이 defined(정의된) 포인터 타입이고(*x).f가 필드(메서드가 아닌)를 가리키는 유효한 셀렉터 식이라면,x.f는(*x).f의 줄임말이 돼요. -
그 외의 모든 경우에
x.f는 illegal이에요. -
x가 포인터 타입이면서 값이nil이고x.f가 구조체 필드를 가리킨다면,x.f에 값을 할당하거나 평가하는 것은 run-time panic(런타임 패닉)을 일으켜요. -
x가 인터페이스 타입이면서 값이nil이라면, 메서드x.f를 호출하거나 평가하는 것은 run-time panic을 일으켜요.
예를 들어, 다음과 같은 선언이 주어졌다고 해볼게요:
type T0 struct {
x int
}
func (*T0) M0()
type T1 struct {
y int
}
func (T1) M1()
type T2 struct {
z int
T1
*T0
}
func (*T2) M2()
type Q *T2
var t T2 // with t.T0 != nil
var p *T2 // with p != nil and (*p).T0 != nil
var q Q = p
이제 다음과 같이 쓸 수 있어요:
t.z // t.z
t.y // t.T1.y
t.x // (*t.T0).x
p.z // (*p).z
p.y // (*p).T1.y
p.x // (*(*p).T0).x
q.x // (*(*q).T0).x (*q).x is a valid field selector
p.M0() // ((*p).T0).M0() M0 expects *T0 receiver
p.M1() // ((*p).T1).M1() M1 expects T1 receiver
p.M2() // p.M2() M2 expects *T2 receiver
t.M2() // (&t).M2() M2 expects *T2 receiver, see section on Calls
하지만 다음은 유효하지 않아요:
q.M0() // (*q).M0 is valid but not a field selector
더 알아보기
- 이어지는 내용으로는 Method expressions(메서드 식)과 Method values(메서드 값)를 함께 보면 셀렉터를 훨씬 깊이 이해할 수 있어요.
- 셀렉터가 어떤 필드를 가리키는지 결정하는 깊이 규칙은 embedded field와 method set을 함께 읽으면 전체 그림이 잡혀요.
- Go 공식 문서의 원문은 Selectors에서 확인할 수 있어요.