구조체 타입

구조체 타입 (Struct types)

여러 값을 한 덩어리로 묶어 표현하고 싶을 때, Go에서는 구조체(struct)를 써요. 이름을 가진 요소들, 즉 필드(field) 의 나열인데요, 각 필드는 저마다 이름과 타입을 갖죠. 이번 섹션에서는 구조체가 어떻게 정의되는지, 필드 이름을 어떻게 붙이는지, 그리고 임베디드 필드와 태그가 하는 일까지 차근차근 살펴볼게요.

출처: Go Specification

본문

구조체는 필드라고 부르는, 이름을 가진 요소들의 나열이에요. 각 필드는 이름과 타입을 갖고 있죠. 필드 이름은 명시적으로 지정할 수도 있고(IdentifierList), 임베디드 필드(EmbeddedField)처럼 묵시적으로 정해질 수도 있어요. 구조체 안에서 blank가 아닌 필드 이름은 서로 유일(unique)해야 합니다.

구조체 타입의 문법은 아래 EBNF처럼 생겼어요.

StructType    = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl     = ( IdentifierList Type | EmbeddedField ) [ Tag ] .
EmbeddedField = [ "*" ] TypeName [ TypeArgs ] .
Tag           = string_lit .
// An empty struct.
struct {}

// A struct with 6 fields.
struct {
	x, y int
	u float32
	_ float32  // padding
	A *[]int
	F func()
}

타입은 가지지만 명시적인 필드 이름이 없는 필드를 임베디드 필드(embedded field) 라고 불러요. 임베디드 필드는 타입 이름 T로 지정하거나, 인터페이스가 아닌 타입 이름에 대한 포인터 *T로 지정해야 해요. 이때 T 자신은 포인터 타입이나 타입 파라미터가 될 수 없습니다. 그리고 한정되지 않은(unqualified) 타입 이름이 그대로 필드 이름이 됩니다.

// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4
struct {
	T1        // field name is T1
	*T2       // field name is T2
	P.T3      // field name is T3
	*P.T4     // field name is T4
	x, y int  // field names are x and y
}

아래 선언은 필드 이름이 구조체 타입에서 유일해야 한다는 규칙 때문에 사용할 수 없어요.

struct {
	T     // conflicts with embedded field *T and *P.T
	*T    // conflicts with embedded field T and *P.T
	*P.T  // conflicts with embedded field T and *T
}

구조체 x의 임베디드 필드에 속한 필드나 메서드 f는, x.f가 그 필드나 메서드 f를 가리키는 합법적인 셀렉터(selector)일 때 승격되었다(promoted) 고 말해요.

승격된 필드는 구조체의 일반 필드처럼 동작합니다.

구조체 타입 S와 타입 이름 T가 주어졌을 때, 승격된 메서드는 다음과 같은 규칙으로 구조체의 메서드 집합(method set)에 포함돼요.

  • S가 임베디드 필드 T를 포함하면, S*S메서드 집합은 모두 리시버가 T인 승격된 메서드를 포함하고, *S의 메서드 집합은 리시버가 *T인 승격된 메서드도 추가로 포함해요.
  • S가 임베디드 필드 *T를 포함하면, S*S의 메서드 집합은 모두 리시버가 T 또는 *T인 승격된 메서드를 포함해요.

필드 선언 뒤에는 선택적으로 문자열 리터럴 태그(tag) 가 올 수 있어요. 태그는 해당 필드 선언에 속한 모든 필드의 속성이 되죠. 빈 태그 문자열은 태그가 없는 것과 동일합니다. 태그는 reflection 인터페이스를 통해 보이며, 구조체의 타입 동일성(type identity)에도 참여하지만 그 외에는 무시돼요.

struct {
	x, y float64 ""  // an empty tag string is like an absent tag
	name string  "any string is permitted as a tag"
	_    [4]byte "ceci n'est pas un champ de structure"
}

// A struct corresponding to a TimeStamp protocol buffer.
// The tag strings define the protocol buffer field numbers;
// they follow the convention outlined by the reflect package.
struct {
	microsec  uint64 `protobuf:"1"`
	serverIP6 uint64 `protobuf:"2"`
}

구조체 타입 T는 타입 T 자체를 필드로 가질 수 없고, T를 구성 요소로 포함하는 타입이 오직 배열이나 구조체 타입뿐이라면 직접적으로든 간접적으로든 그런 타입도 필드로 가질 수 없어요.

// invalid struct types
type (
	T1 struct{ T1 }            // T1 contains a field of T1
	T2 struct{ f [10]T2 }      // T2 contains T2 as component of an array
	T3 struct{ T4 }            // T3 contains T3 as component of an array in struct T4
	T4 struct{ f [10]T3 }      // T4 contains T4 as component of struct T3 in an array
)

// valid struct types
type (
	T5 struct{ f *T5 }         // T5 contains T5 as component of a pointer
	T6 struct{ f func() T6 }   // T6 contains T6 as component of a function type
	T7 struct{ f [10][]T7 }    // T7 contains T7 as component of a slice in an array
)

더 알아보기