在Go语言中,命名返回切片是否需要使用make进行初始化?

huangapple go评论83阅读模式
英文:

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

问题

假设有这样一个函数:

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

我可以直接在代码中执行以下操作,就好像s已经被创建了一样。

s = append(s, 5)

但是如果我不执行上述的append操作,那么s始终为nil,返回的s也是nil

为什么会设计成这样?这看起来非常不一致。

英文:

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.

答案1

得分: 3

命名返回切片被初始化为nil。以下语句有效,因为append函数对待nil切片和空切片的方式相同。

 s = append(s, 5)

由于nil切片的长度和容量都定义为零,所以处理nil切片和空切片的方式相同。

这个特性与命名返回值无关。以下是一个没有返回值的演示:

 var x []int           // x是一个nil切片
 fmt.Println(x)        // 输出[]
 fmt.Println(x == nil) // 输出true
 x = append(x, 5)      // x是一个包含一个元素5的切片
 fmt.Println(x)        // 输出[5]
 fmt.Println(x == nil) // 输出false

在检查这些特性时容易混淆的一点是,fmt包将nil切片和空切片都打印为相同的表示形式[]

英文:

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, [].

答案2

得分: 2

你可以将项目附加到nil切片中,无论它是nil还是非nil。

看下面的示例:

package main

import "log"

func main() {
	a := a() // 无论它返回nil
	log.Println("a是否为nil:", a == nil)
	a = append(a, 1) // 你可以将项目附加到nil切片中
	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
}

结果为:

2022/06/19 09:52:02 a是否为nil true
2022/06/19 09:52:02 [1]
2022/06/19 09:52:02 [5 2]
英文:

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]

答案3

得分: 1

在Go语言中,任何具名返回变量(也称为“结果参数”,根据规范的说法)都会被“预初始化”为相应类型的零值,与任何没有初始化器的局部变量完全相同:

var i int
var f float64
var s string

i 是零(整数),f 是零(0.0,float64),s 是“零”(空字符串 "")。所以对于你的函数也是一样的:

func returnNamedSlice(num int) (s []int) {
    // ... 在这里编写代码 ...
    return
}

在函数的顶部,也就是 // 在这里编写代码 的地方,s 被初始化为“零”,即 []int(nil)nil 被转换为 []int 类型。这在Go规范的返回语句部分有描述。

你必须将 s 设置为某个非 nil 值,以返回一个非 nil 值。你不必使用 append,但通常会将元素追加到适当类型的 nil 上,因为这样可以很容易地在循环或一系列 if 测试中构建列表:

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

但是:

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

也可以工作。

无论最终将 s 设置为什么值,它在返回时都将保持该值。请注意:

return someval

意味着(1)将值赋给 s,然后(2)返回(按照这个顺序)。如果延迟函数随后调用 panic并且有一个 panic 处理程序捕获了 panic 并使用 s 和/或为 s 赋予了新值,这一点很重要;请参阅规范中的延迟语句示例

英文:

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.

huangapple
  • 本文由 发表于 2022年6月19日 12:12:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/72674226.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定