What about memory layout means that []T cannot be converted to []interface in Go?
Asked Answered
D

2

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?

Dottiedottle answered 13/3, 2015 at 6:36 Comment(0)
G
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!
Gearbox answered 13/3, 2015 at 6:53 Comment(13)
The last code part nails it: it would break type-safety without touching reflection. +1Sepsis
@Sepsis 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{}<String> ;)Gearbox
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 pointersDottiedottle
@Dottiedottle no, MyType is not 2 words long. Only an interface{} is two words long (as illustrated by the diagram in the answer).Gearbox
@Gearbox 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??Dottiedottle
@Dottiedottle 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.Gearbox
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?Dottiedottle
@Dottiedottle what "other interfaces" are you referring to?Gearbox
for example, type Reader interface { Read(p []byte) (n int, err error) }Dottiedottle
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).Gearbox
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?Dottiedottle
Let us continue this discussion in chat.Dottiedottle
@Dottiedottle I have to call it a day. I will follow your newest question (I didn't saw immediately): https://mcmap.net/q/671660/-how-are-interfaces-represented-in-go/6309Gearbox
S
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.

Sepsis answered 13/3, 2015 at 6:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.