golang 에서 nil 이란 무엇인가
결론
- zero value는 명시적인 초기값을 할당하지 않고 변수를 만들었을 때 해당 변수가 갖게 되는 값이다.
- nil은 포인터, 인터페이스, 맵, 슬라이스, 채널, 함수의 zero value이다.
그저 어떤 값이 빈 값인지(파이썬으로 치자면 None인지)확인하고 싶은데, 뜬금없이 컴파일 에러가 날 때가 있다.
왜 이런 일이 발생하는지 알기 위해서는 golang에서 nil이 무엇인지 알아야 한다.
우리의 친구 스택오버플로에는 이미 누군가 동일한 질문을 올려놨고, 누군가 고마운 분이 답변을 달아놓았다.
https://stackoverflow.com/questions/35983118/what-does-nil-mean-in-golang
In Go, nil is the zero value for pointers, interfaces, maps, slices, channels and function types, representing an uninitialized value.
zero value란 무엇인가? 명시적인 초기값 없이 선언된 변수는 zero value를 갖게 된다.
예를 들어 string의 경우에는 ""이다. 빈 문자열이 아닌 것에 주의한다.
물론 빈 문자열이다. 말장난같지만, 포인트는 유저/프로그래머 등이 의도적으로 빈 값을 넣은 게 아니라는 것이다.
명시적인 값을 할당해주지 않았기 때문에, 빈 문자열을 zero value로 갖는다는 것이다.
a tour of go에도 zero value에 대한 언급이 있다.
https://tour.golang.org/basics/12
스트링의 zero value는 ""라고 했다.
package main
import "fmt"
func main() {
var s string
fmt.Printf(s == nil)
// ./prog.go:7:15: invalid operation: s == nil (mismatched types string and nil)
}
위 예제 코드를 실행하면 에러가 난다. 애초에 스트링 타입은 nil을 가질 수가 없다.
왜 그럴까? nil은 포인터, 인터페이스, 맵, 슬라이스, 채널, 함수 타입의 zero value이기 때문이다.
다른 예제 코드를 보자.
https://play.golang.org/p/sh5oendMW5c 에서 실행해볼 수 있다.
package main
import "fmt"
func main() {
var a []int
b := make([]int, 0)
c := []int{}
var d []int
d = nil
e := make([]int, 0, 0)
f := make([]int, 0, 1)
fmt.Println(a == nil, // true
b == nil, // false
c == nil, // false
d == nil, // true
e == nil, // false
f == nil) // false
}
a와 d는 true, 그 외의 변수들은 false가 출력된다.
왜 이렇게 출력될까? zero value의 정의를 다시 떠올려 보자.
명시적인 초기값 없이 선언된 변수는 zero value를 갖게 된다고 했다.
a는 명시적인 초기값이 있는가? 없다.
b는? 있다. c는? 있다. 초기값을 할당해줬다.
e, f도 우리는 초기값을 잘 할당해 줬다.
(d는 사실 원래 값이 nil인데 한번더 nil이라고 할당해주고 있다.)
따라서 a == nil , d == nil만이 true이다.
그렇다면 아래 코드는 어떨까?
https://play.golang.org/p/6mF1wAADVvQ 에서 실행해볼 수 있다.
package main
import "fmt"
func main() {
var a []int
var b []int
fmt.Println(a == nil) // true
// fmt.Println(a == b) // invalid operation: a == b (slice can only be compared to nil)
}
주석 처리된 fmt.Println(a == b)의 주석을 제거하고 프로그램을 실행해보면, 컴파일 에러가 발생한다.
분명히 a도 nill 이고 b도 nill이니까 true가 출력되어야할 것 같은데, 애초에 빌드 자체가 실패한다.
에러 메세지에도 나와있듯이, slice는 nil하고의 비교만 가능하다. 서로 다른 두 slice를 == 연산자를 이용하여 비교할 수 없기 때문이다.
(물론 파이썬에서는 된다.)
+)
json serializing 하면 nil slice는 null로, empty slice는 []로 대응되었던 것 같다는 제보를 입수.
비어있는 것과 애초에 아무 것도 없는 것은 분명히 다르다.