August Feng

go functions receiving nils

About

A few gotchas when using nil as function arguments in golang.

Studies

The zero value of a slice is nil:

  var s []string
  if s == nil {
  	fmt.Println("s is nil!") // XXX: we will see this printed.
  }

However, when s is passed to a function that accepts an interface, then it is no longer true!

  func run(something any) {
  	if something == nil {
  		fmt.Println("something is nil!")
  	} else {
  		fmt.Println("something is not nil!")
  	}
  }

  func main() {
  	var s []string
  	run(s) // XXX: s is not nil!

This is because in any is an interface, and a comparison operation on an interface compares two things: the interface's type, and the interface's value.

The s object is concrete, but it becomes an interface once receivec by the function.

Inside the function execution, it has a []string type and a nil value.

  func run(something any) {
  	t := reflect.TypeOf(something)
  	v := reflect.TypeOf(something)
  	fmt.Printf("something is type: %s\n", t)
  	fmt.Printf("something has value: %s\n", t)
  }


  func main() {
  	var s []string
  	run(s) // XXX: s is type: []string, s has value: []
  }

Strange! We would think s has nil value? The nil-ness (is this a word?) is only available to some types and we need to narrow it:

  func run(something any) {
  	v := reflect.TypeOf(something)
  	switch val.Kind() {
    case reflect.Pointer, reflect.Slice, reflect.Map, reflect.Chan, reflect.Interface, reflect.Func:
    	if val.IsNil() {
    		fmt.Println("something is nil.")
    	} else {
    		fmt.Println("somethingis not nil")
    	}
    }
  }

  func main() {
  	var s []string
  	run(s) // XXX: s is nil.
  }

Solution

So, if it's not easy to get determine the nil-ness of a concrete value that's passed through an interface, what can we do?

Pass interfaces instead! or if you can remember, the nil literal itself!

  func run(something any) {
  	if something == nil {
  		fmt.Println("something is nil!")
  	} else {
  		fmt.Println("something is not nil!")
  	}
  }

  func main() {
  	var r io.Reader
  	run(r) // XXX: r is nil!
  	run(nil) // XXX: r is nil!
  }