在可变参数函数中混合使用“展开”切片和常规参数。

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

mixing "exploded" slices and regular parameters in variadic functions

问题

我想知道为什么在Go语言中无法执行以下操作:

func main() {
    stuff := []string{"baz", "bla"}
    foo("bar", stuff...)
}

func foo(s ...string) {
    fmt.Println(s)
}

在我理解中,slice...会将切片“展开”,以便在多参数函数调用中使用。因此,上面的示例实际上应该展开为foo("bar", "baz", "bla")

foo(stuff...)按预期工作,没有什么意外,但在上面的示例中,编译器会报告参数过多的错误。

这是一种有意的限制吗?我来自Ruby背景,foo("bar", *stuff)在Ruby中是完全可以的(至少在我看来是一样的),所以这让我感到惊讶。

英文:

I'm wondering why it's not possible to do the following in go:

func main() {
	stuff := []string{"baz", "bla"}
	foo("bar", stuff...)
}

func foo(s ...string) {
	fmt.Println(s)
}

In my understanding, slice... "explodes" the slice so it can be used for multi argument function calls. So the above example should actually expand to foo("bar", "baz", "bla").

foo(stuff...) works as expected, no surprises here, but in the example above, the compiler complains about too many arguments.

Is this a desired limitation? I'm coming from a ruby background where a foo("bar", *stuff) is perfectly fine (and is, at least in my book, the same thing), that's why this surprises me.

答案1

得分: 23

可变参数的值可以通过列举元素或使用现有的切片来指定。

你想要混合使用这两种方式,但这在 Go 语言规范中是不允许的(参见将参数传递给 ... 参数)。

如果使用第一种形式(列举元素):

传递的值(作为可变参数)是一个新的 []T 类型的切片,其底层数组的连续元素是实际的参数。

如果使用后一种形式(传递一个现有的切片后跟 ...),不会创建新的切片,而是直接使用你传递的切片。而且传递的切片只能用于指定一个可变参数,即最后一个可变参数。如果尝试同时传递单个元素和切片,将无法匹配函数的签名(在这种情况下是参数列表),并且会出现错误:

调用 foo 时参数过多

在 Go 中并没有实际的“展开”操作,这个术语只是用于其他语言中,以帮助可视化传递的数组或切片不是可变参数的一个_元素_,而是可变参数_本身_的值。

混合使用这两种方式需要分配一个新的切片,因为显然不能使用现有的切片。

英文:

The value for a variadic argument can be specified either by enumerating the elements, or using an existing slice, specified by its name followed by ....

You want to mix the 2 possible ways which is not permitted by the Go Language Specification (Passing arguments to ... parameters).

If the first form is used (enumerating the elements):

> The value passed [as the variadic parameter] is a new slice of type []T with a new underlying array whose successive elements are the actual arguments.

If the latter is used (passing an existing slice followed by ...) no new slice is created, the one you pass is used as is. And the passed slice can only be used to specify the value of one – the final – variadic parameter. Attempting to pass both a single element and a slice will not match the signature (the parameter list in this case) of your function and you'll get an error:

too many arguments in call to foo

There is no actual "exploding" involved in Go, the term is just used in other languages to help visualize that the passed array or slice will not be an element of the variadic parameter but will be the value of variadic parameter itself.

Mixing the 2 would require to allocate a new slice because obviously the existing slice cannot be used.

答案2

得分: 21

将其变为新的可变参数的方法是比较糟糕的。

foo(append([]string{"bar"}, stuff...)...)

如果顺序不重要的话:

foo(append(stuff, "bar")...)

https://play.golang.org/p/mY6y0vScfPB

英文:

The ugly way to get this to work is make it into a new variadic.

foo(append([]string{"bar"}, stuff...)...)

And if the order doesn't matter:

foo(append(stuff, "bar")...)

https://play.golang.org/p/mY6y0vScfPB

答案3

得分: 5

这个问题的规范在“将参数传递给...参数”中:

如果f是具有类型为...T的最后一个参数p的可变参数函数,那么在f内部,p的类型等同于[]T类型。
如果在调用f时,没有为p提供实际参数,则传递给p的值为nil
否则,传递的值是一个新的[]T类型的切片,其底层数组的连续元素是实际的参数,而且这些参数都必须可以赋值给T

在你的情况下,stuff...可以工作:

如果最后一个参数可以赋值给切片类型[]T,并且在参数后面跟着...,那么它可以不变地作为...T参数的值传递。在这种情况下,不会创建新的切片。

但是"bar", stuff...不符合上述任何一种情况。

T, []T不匹配f([]T)

英文:

The specification on this is at the "Passing arguments to ... parameters":

> If f is variadic with a final parameter p of type ...T, then within f the type of p is equivalent to type []T.
If f is invoked with no actual arguments for p, the value passed to p is nil.
Otherwise, the value passed is a new slice of type []T with a new underlying array whose successive elements are the actual arguments, which all must be assignable to T.

In your case, where stuff... works:

> If the final argument is assignable to a slice type []T, it may be passed unchanged as the value for a ...T parameter if the argument is followed by .... In this case no new slice is created.

But "bar", stuff... doesn't match either case specified above.

T, []T doesn't match f([]T).

答案4

得分: 1

我在准备传递给外部命令的参数时遇到了这种情况。如果可能的话,只需构建一个参数切片,这样在调用函数时就不必担心将标量与切片组合在一起:

package main
import "os/exec"

func main() {
   stuff := []string{"bar"}
   stuff = append(stuff, "baz", "bla")
   exec.Command("name", stuff...).Run()
}
英文:

I ran into this situation when preparing arguments to feed to external commands. If possible, just build an one argument slice, then you don't have to worry about combining scalars with slices when it's time to call the function:

package main
import "os/exec"

func main() {
   stuff := []string{"bar"}
   stuff = append(stuff, "baz", "bla")
   exec.Command("name", stuff...).Run()
}

huangapple
  • 本文由 发表于 2015年2月20日 17:26:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/28625546.html
匿名

发表评论

匿名网友

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

确定