본문 바로가기
Language/Go

Empty struct

by ocwokocw 2022. 6. 21.

- 출처: https://dave.cheney.net/2014/03/25/the-empty-struct

- Introduction

empty struct란 말 그대로 field가 하나도 없는 struct type 이다. 
 
type Q struct{}
var q struct{}
 
근데 여기서 한 가지 의문점이 생긴다. field도 없고 데이터도 포함되어있지 않은 struct를 왜 사용하는것인가?
 

- Width

empty struct를 알아보기 전에 width에 대해서 알아보자. width 라는 용어는 gc 컴파일러에서 유래했는데, type의 instance를 저장할 때 필요한 byte 수라고 할 수 있다. 이 블로그의 원문을 쓴 필자는 프로세스의 주소 공간은 1차원이기 때문에 width가 size보다 더 적절한 용어라는 견해도 더불어 밝히고 있다.
 
width는 type의 속성이다. Go 프로그램에서 모든 값은 type을 갖는데, 값의 width는 해당 type에 의해서 정의되며 언제나 8의 배수이다. 모든 값의 width를 계산할 수 있는데, unsafe.Sizeof() 함수를 사용하면 된다.
 
var s string
var c complex128
fmt.Println(unsafe.Sizeof(s))	 // prints 8
fmt.Println(unsafe.Sizeof(c))	 // prints 16
 
주석은 string type s의 경우 8을 출력한다고 되어있지만 실제로 돌려보면 16이 나온다.
 
array type의 width는 해당 요소 type을 곱한만큼이 된다.
 
var a [3]uint32
fmt.Println(unsafe.Sizeof(a)) // prints 12
 
struct는 복합(composite) type을 정의할 때 유용한데, width는 구성 유형의 합에다가 padding이 더해진다.
 

- empty struct

지금까지 width를 살펴보았는데 이로써 알 수 있는것은 empty struct가 0의 width를 갖는다는 사실이다. empty struct는 0 byte의 저장공간을 점유한다. 
 
var s struct{}
fmt.Println(unsafe.Sizeof(s)) // prints 0
 
empty struct는 0 byte를 소비하므로 padding을 필요로 하지 않는다. 그래서 empty struct로 구성된 중첩 struct 또한 저장 공간을 점유하지 않는다.
 
type S struct {
        A struct{}
        B struct{}
}
var s S
fmt.Println(unsafe.Sizeof(s)) // prints 0
 

- empty struct로 무엇을 할 수 있는가?

Go는 직교성을 띄기 때문에 empty struct 또한 다른 struct type과 별로 다를바가 없다. 그래서 struct를 사용할 때 일반적으로 사용하는 특징을 똑같이 empty struct에도 적용할 수 있다.
 
아래와 같이 sturct{}들의 배열을 선언해도 저장공간을 점유하지 않는다.
 
var x [1000000000]struct{}
fmt.Println(unsafe.Sizeof(x)) // prints 0
 
struct{}의 slice는 그들의 header를 위한 공간만을 차지한다. 
 
var x = make([]struct{}, 1000000000)
fmt.Println(unsafe.Sizeof(x)) // prints 12 in the playground
 
12를 출력한다고 되어있지만 실제로 돌려보면 24가 나온다.
 
물론 일반 subslice에서 len, cap 과 같은 빌트인도 생각한대로 잘 동작한다. 
 
var x = make([]struct{}, 100)
var y = x[:50]
fmt.Println(len(y), cap(y)) // prints 50 100
 
&(addressable)을 이용해서 struct{} 값의 주소 공간을 취할수도 있다. 그리고 두개의 struct{} 값의 주소가 같은것도 확인할 수 있다. 라고 되어있지만 현재는 실행해보면 false를 반환한다. 
 
var a, b struct{}
fmt.Println(&a == &b) // true
 
[]struct{} 의 특성도 확인할 수 있다.
 
a := make([]struct{}, 10)
b := make([]struct{}, 20)
fmt.Println(&a == &b)       // false, a and b are different slices
fmt.Println(&a[0] == &b[0]) // true, their backing arrays are the same
 

- 메소드 리시버로서의 struct{}

empty struct가 메소드 리시버로서 어떻게 사용되는지를 알아보자.
 
type S struct{}

func (s *S) addr() { fmt.Printf("%p\n", s) }

func main() {
        var a, b S
        a.addr() // 0x1beeb0
        b.addr() // 0x1beeb0
}
 
위의 예제에서 주소가 0x1beeb0을 출력하고 있다.
 
 

'Language > Go' 카테고리의 다른 글

Go - pipelines and cancellation  (0) 2022.10.21
package name base, util, or common  (0) 2022.06.26
Zero value  (0) 2022.06.17
Error handling을 간단하게  (0) 2022.06.16
Exception  (0) 2022.06.14

댓글