Golang의 nil과 interface nil의 재미있는 특징

Null은 일반적으로 존재하지 않는 값 또는 메모리 주소를 의미하며 언어에 따라 null nil nan none undefined 등 여러가지 형태로 표현됩니다. 다만 null이란 개념이 언어마다 특성과 철학, 표현하는 방식에 따라서 달라지기도 하는데요. 오늘은 golang에서의 null인 nil의 특성 하나를 살펴보려고 합니다.

zero value

먼저 zero value에 대해 알고가면 이해에 좋습니다. zero value는 초기값을 가지지 않고 변수를 만들었을 때 해당 변수가 가질 값을 의미합니다.

nil

nil은 pointer, interface, slice, channel, func의 zero value 입니다.

그래서 아래와 같이 string 등의 zero value는 공백이지만, slice를 만들어서 확인해보면 nil을 zero value를 가집니다.

package main

import "fmt"

func main(){
  var a string
  fmt.Println(a)
  // 아래 코드처럼 nil과 비교하면 에러가 발생합니다.
}
package main

import "fmt"

func main(){
  var b []string
  fmt.Println(b == nil)
}

Interface nil

아래 코드는 interface를 zero value로 만들고 nil인지 비교하는 코드인데, 어떤 결과가 나올까요?

package main

import (
	"fmt"
)

type A struct {}

func makeNil() interface{} {
	var a A
	return a
}

func main() {
	n := makeNil()
	if n == nil {
		fmt.Println("makeNil() is nil")
	} else {
		fmt.Println("makeNil() is not nil")
	}
}

눈으로만 본다면 makeNil() is nil가 나올 것 같지만, 실제론 아래 메시지가 출력됩니다.

makeNil() is not nil

이는 interface가 Type: Value,와 같이 type을 저장하는 T 값과 value를 저장하는 V 2가지를 사용해서 데이터를 저장하는데 zero value로 인해 value 가 nil이 되어도 주소값을 저장해야하는 type은 nil을 줄 수 없기 때문에 nil이 아니게 됩니다.

그래서 아래와 같이 Typeof로 interface 를 찍어보면 nil이 아닌 main.A 가 찍힙니다.

func makeNil() interface{} {
	var a A
	fmt.Println(reflect.TypeOf(a))
	return a
}

그래서 makeNil()의 결과는 nil이 아니게 됩니다.

Conclusion

개발하다보면 충분히 실수할 수 있는 부분이라 잘 알아두고 있는게 좋을 것 같습니다 :D

References