-1

What is the reason that, given a (variadic) function

func varargs(n ...int) {}

it can be called like

varargs(1, 2, 3, 4) // Fixed number of arguments

but not with an array:

a := [4]int{1, 2, 3, 4} // Fixed number of elements
varargs(a...) // Error: cannot use (type [4]int) as type []int in argument

I understand why

var s []int = a

wouldn't work: it prevents accidental misuse, requiring manual slicing:

s := a[:]

But why does this restriction extend to calls to variadic functions?


Bonus question:
Conversely, why would calling

func fourargs(w, x, y, z int) {}

with a 4-element array like

fourargs(a...) // Error: not enough arguments in call  have ([4]int...)  
               //                                      want (int, int, int, int)

also be forbidden?
It can be type-checked at compile time.

P Varga
  • 19,174
  • 12
  • 70
  • 108

1 Answers1

8

Spec: Passing arguments to ... parameters:

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.

So when you have a slice and you pass it as the value of the variadic parameter, no new slice is created, it is just assigned.

If you have an array, that is a different type, that is not assignable to a slice type. Therefore it is not allowed.

You must first slice the array, which you can do without an intermediate variable:

a := [4]int{1, 2, 3, 4}
varargs(a[:]...)

Yes, you could say this automatic slicing could happen automatically / internally. Why this isn't allowed is opinion based (and belongs to the authors of Go). Basically, in Go arrays are secondary. Slices are the way to go. You should have a slice in the first place, which you can pass and you don't have a problem. See related questions: Why have arrays in Go? and Slicing a slice pointer passed as argument.

icza
  • 389,944
  • 63
  • 907
  • 827
  • I find the "no hidden performance cost" argument (no new slice created) convincing, actually. – P Varga Apr 12 '19 at 12:16
  • 1
    @ᆼᆺᆼActually creating a slice is not much overhead, it's just a tiny header (see [How to inspect slice header?](https://stackoverflow.com/questions/54195834/how-to-inspect-slice-header/54196005#54196005)), when you pass a "ready" slice that also requires copying the slice header. – icza Apr 12 '19 at 13:13
  • Perhaps there is an overarching "no implicit conversions" philosophy then? – P Varga Apr 12 '19 at 13:50
  • @ᆼᆺᆼNot sure what you mean. Can you please elaborate? – icza Apr 12 '19 at 13:51
  • I mean Go prefers explicit conversions (eg. no mixing of `int` & `float`), so maybe not automatically slicing an array is in keeping with the same philosophy (I see how this is opinion based). – P Varga Apr 12 '19 at 14:32
  • 1
    @ᆼᆺᆼ`int` to `float` can be achieved with a type conversion. Array to slice is not a conversion, it's a slicing operation. They are not the same. – icza Apr 12 '19 at 14:33