1

So I've been reading these two articles and this answer

Cannot convert []string to []interface {} says that the memory layout needs to be changed.

http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go says that understanding the underlying memory makes answering this question easy, and

http://research.swtch.com/interfaces, explains what is going on under the hood.

But for the life of me I can't think of a reason, in terms of the implementation of interfaces as to why []T cannot be cast to []interface.

So Why?

Community
  • 1
  • 1
praks5432
  • 7,246
  • 32
  • 91
  • 156

2 Answers2

4

The article "InterfaceSlice" try to detail:

A variable with type []interface{} is not an interface! It is a slice whose element type happens to be interface{}. But even given this, one might say that the meaning is clear.

Well, is it? A variable with type []interface{} has a specific memory layout, known at compile time.

Each interface{} takes up two words (one word for the type of what is contained, the other word for either the contained data or a pointer to it). As a consequence, a slice with length N and with type []interface{} is backed by a chunk of data that is N*2 words long.

See also "what is the meaning of interface{} in golang?"

2 words

This is different than the chunk of data backing a slice with type []MyType and the same length. Its chunk of data will be N*sizeof(MyType) words long.

The result is that you cannot quickly assign something of type []MyType to something of type []interface{}; the data behind them just look different.

"why []string can not be converted to []interface{} in Go" adds a good illustration:

// 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!
Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • The last code part nails it: it would break type-safety _without_ touching reflection. +1 – icza Mar 13 '15 at 07:09
  • @icza Indeed. So it isn't *only* about memory allocation. It is also because at *compile* time, Go isn't able to detect that `sliceOfInterface[0]` (which is an `interface{}`) `= 1` is invalid. If only Go had some "generic" way to specify the type of `[]interface{}` ;) – VonC Mar 13 '15 at 07:41
  • wait, so I get the last part, it's similar to covariance of arrays right? You break type safety because you can now treat an integer as a string, when it can't be treated in that way. However, the part about N*sizeof(MyType) - in that case, MyType is STILL 2 words long, there are just two pointers – praks5432 Mar 13 '15 at 17:47
  • @praks5432 no, MyType is *not* 2 words long. Only an `interface{}` is two words long (as illustrated by the diagram in the answer). – VonC Mar 13 '15 at 17:50
  • @VonC but that diagram represents a general interface no? There are still two pointers. In the interface{} case, one points to data and one points to the underlying type. In other interfaces, one points to a method table, one points to data?? – praks5432 Mar 16 '15 at 21:20
  • @praks5432 the answer stands: in your case, `MyType` is *not* an interface. `interface{}`, or other interfaces, are two words long. Memory-wise, you still cannot pass from one to the other easily. – VonC Mar 16 '15 at 21:25
  • Wait..I don't get it. They're both two words long, so N*sizeof(MyType) === N*2. So really, what you're saying is regardless of the size (which is the same), under the hood, interface{} points to data and an underlying type(is this concrete), and the other case points to an itable instead. So you cannot convert. How does that play with the example you gave with strings and integers? – praks5432 Mar 16 '15 at 21:28
  • @praks5432 what "other interfaces" are you referring to? – VonC Mar 16 '15 at 21:31
  • for example, type Reader interface { Read(p []byte) (n int, err error) } – praks5432 Mar 16 '15 at 21:41
  • Ok. And how `N*sizeof(MyType) === N*2`? All the answers here point out that the memory size isn't the same between an array of interface (any interface) and an array of non-interface type (like `MyType`). – VonC Mar 16 '15 at 21:47
  • Wait. So does T refer to only non-interface type? What is a non-interface type? Can I convert []T to []interface if T is an interface type, as the underlying memory would look the same? – praks5432 Mar 16 '15 at 21:50
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/73112/discussion-between-praks5432-and-vonc). – praks5432 Mar 16 '15 at 21:55
  • @praks5432 I have to call it a day. I will follow your newest question (I didn't saw immediately): http://stackoverflow.com/q/29087450/6309 – VonC Mar 16 '15 at 22:00
1

Read the blog article The Laws of Reflection, section The representation of an interface.

A variable of interface type stores a pair: the concrete value assigned to the variable, and that value's type descriptor. To be more precise, the value is the underlying concrete data item that implements the interface and the type describes the full type of that item.

So if you have a value of []T (a slice of T) where T is not an interface, the elements of such a slice only stores values of type T, but it does not store the type information, it belongs to the slice type.

If you have a value of type []inteface{}, the elements of such a slice holds the concrete values and the type descriptors of those values.

So elements in a []interface{} require more info (more memory) than in a non-interface []T. And if the occupied memory of those 2 slices are not the same, they cannot be just "looked at" differently (looked at as a differnet type). Producing one from the other requires additional work.

icza
  • 389,944
  • 63
  • 907
  • 827