Home > database >  Does the named return slice need to be initialized with make in Go?
Does the named return slice need to be initialized with make in Go?

Time:06-20

Suppose a function such as:

func returnNamedSlice(num int) (s []int)

I am able to do the following directly in the code, as if s was already made.

s = append(s, 5)

But if I don't do the above (append operation), then s is always nil and the returned s is also nil.

Why this design? This seems very inconsistent.

CodePudding user response:

The named return slice is initialized to nil. The following statement works because a nil slice is handled the same as an empty slice by the append function.

 s = append(s, 5)

Nil slices are handled the same as empty slices because the length and capacity of a nil slice are defined to be zero, the same as an empty slice.

The feature is unrelated to named return values. Here's a demonstration without return values:

  var x []int           // x is a nil slice of int
  fmt.Println(x)        // prints []
  fmt.Println(x == nil) // prints true
  x = append(x, 5)      // x is slice with one element, 5
  fmt.Println(x)        // prints [5]
  fmt.Println(x == nil) // prints false

A confusing point when examining these features is that the fmt package prints nil slices and empty slices with the same representation, [].

CodePudding user response:

You can append items to nil slice too, no matter it's nil or not.

Look at the example below:

package main

import "log"

func main() {
    a := a() // no matter it returns nil 
    log.Println("Is a nil: ", a == nil)
    a = append(a, 1) // you can append items to nil slice
    log.Println(a)
    //
    b := b()
    b = append(b, 2)
    log.Println(b)
}

func a() (s []int) {
    return
}

func b() (s []int) {
    s = append(s, 5)
    return
}

And the result:

2022/06/19 09:52:02 Is a nil:  true
2022/06/19 09:52:02 [1]
2022/06/19 09:52:02 [5 2]

CodePudding user response:

Any named-return-variable ("result parameter", as the spec calls it) in Go is "pre-initialized" to the appropriate zero value, exactly the same as any locally-defined variable that has no initializer:

var i int
var f float64
var s string

i is zero (integer), f is zero (0.0, float64), and s is "zero" (empty string ""). So it is with your function:

func returnNamedSlice(num int) (s []int) {
    // ... code here ...
    return
}

At the top of the function, where the // code here code goes, s is initialized to "zero", i.e., []int(nil): nil as converted to []int. This is described under the Return statements section of the Go spec.

You must set s to some non-nil value to return some non-nil value. You do not have to use append but it's pretty standard to append to the appropriately typed nil since that makes it easy to build up a list in a loop or with a series of if tests or whatever:

if conditionA {
    s = append(s, 42)
}
if conditionB {
    s = append(s, 6, 9)
}

But:

if conditionC {
    s = []int{3, 1, 4, 1, 5, 9}
}

will work too.

Whatever s is set to in the end, that's what it will hold at return time. Note that:

return someval

means (1) assign the value to s, and then (2) return (in that order). If a deferred function then calls panic and there's a panic handler that traps the panic and uses s and/or assigns a new value to s, this matters; see the Defer statement examples in the spec.

  • Related