6

I found its weird, why []string can not be converted to []interface{} ?

I think it should be possible, because:

  1. they are all slices
  2. every element of []string is string, which of course is interface{}

but in the example below, it will be a compilation error

func f(args ...interface{}){

}
s := []string{"ssd", "rtt"}
f(s...)

why the language can't finish the conversion automatically?

Stephen Weinberg
  • 51,320
  • 14
  • 134
  • 113
zhaozhi
  • 1,491
  • 1
  • 16
  • 19
  • Golang arrays do not support covariance - see http://stackoverflow.com/questions/3839335/any-sensible-solution-to-the-lack-of-array-slice-covariance-in-go?rq=1 , http://stackoverflow.com/questions/19389629/golang-go-get-the-type-of-array – user2864740 Jan 24 '14 at 05:36
  • This is described in the FAQ http://golang.org/doc/faq#convert_slice_of_interface . Reading the documentation provided on golang.org cannot be over-recommended. – Volker Jan 24 '14 at 15:59
  • duplicate of http://stackoverflow.com/q/12990338/727643 – Stephen Weinberg Jan 25 '14 at 00:30

2 Answers2

7

Because []string and []interface{} have different in-memory layouts. This is obvious when you realise that an interface{} variable needs to know the type of the value it contains.

For a []string slice, the backing array only needs to hold the individual strings. For the []interface{} slice, you've got both type information and the string values (well, pointers to the string values, since a string is larger than a single word of memory). So converting from one type to the other will involve copying the data.

It would be confusing for Go to automatically perform a conversion, since it would make it difficult to reason about code. For example, a function call f(s) could modify the strings in the slice s if it was declared to take a []string argument, but not if it was declared to take a []interface{} argument.

James Henstridge
  • 42,244
  • 6
  • 132
  • 114
7

Slice is basically just a reference to the underlying array, start pointer, length and capacity. So if it would be possible, then consider the following:

sliceOfStrings := []string{"one", "two", "three"}
// prints ONE TWO THREE
for i := range sliceOfStrings {
    fmt.Println(strings.ToUpper(sliceOfStrings[i]))
}

// imagine this is possible
var sliceOfInterface = []interface{}(sliceOfStrings)
// since it's array of interface{} now - we can do anything
// let's put integer into the first position
sliceOfInterface[0] = 1
// sliceOfStrings still points to the same array, and now "one" is replaced by 1
fmt.Println(strings.ToUpper(sliceOfStrings[0])) // BANG!

This issue exists in Java and C#. In practice it happens rarely, but still. Given that in Go there is no automatic type conversions like int32 -> int64 it makes sense that you are forced to create a []interface{} copy if you really want to send []string as []interface{}. This way there can be no surprise - you wrote it explicitly, you know what you're doing. And if function will modify []interface{} - it won't hurt original []string.

Kluyg
  • 5,119
  • 2
  • 25
  • 28