在可变函数中混合 "exploded" 切片和常规参数

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... "explodes" slice 因此它可以用于多参数函数调用。所以上面的例子实际上应该扩展到foo("bar", "baz", "bla")

foo(stuff...) 按预期工作,这并不奇怪,但在上面的示例中,编译器抱怨参数太多。

这是期望的限制吗?我来自 ruby 背景,其中 foo("bar", *stuff) 非常好(而且,至少在我的书中,是同一件事),这就是为什么这让我感到惊讶。

这方面的规范在“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.

在你的情况下,东西...在哪里工作:

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.

但是 "bar", stuff... 与上面指定的任何一种情况都不匹配。

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

可以通过枚举元素指定可变参数的值,使用现有切片,由其名称指定其次是 ....

您想混合 Go 语言规范 (Passing arguments to ... parameters) 不允许的 2 种可能方式。

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

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.

如果使用后者(传递现有切片后跟 ...没有创建新切片,您传递的切片将按原样使用。并且传递的切片只能用于指定 onefinal – 可变参数的值。尝试同时传递单个元素 切片将与函数的签名(在本例中为参数列表)不匹配,您将收到错误消息:

too many arguments in call to foo

Go 中没有实际的 "exploding",这个术语只是用在其他语言中,以帮助形象化传递的数组或切片不会是 元素可变参数的值,但将是可变参数 本身 .

的值

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

让它工作的丑陋方法是将它变成一个新的可变参数。

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

如果顺序无关紧要:

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

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

我 运行 在准备提供给外部命令的参数时遇到这种情况。如果可能的话,只构建一个单参数切片,然后你不必担心在调用函数时将标量与切片组合起来:

package main
import "os/exec"

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