teh-cmc / go-internals

A book about the internals of the Go programming language.
Other
7.83k stars 352 forks source link

chapter2: Interface conversions. Type assertions where the destination type is itself an interface. #34

Open Quuxplusone opened 5 years ago

Quuxplusone commented 5 years ago

It's quite possible this is covered in Chapter 2 or elsewhere, and I missed it, because I am lazy. If so, apologies!

Based on my C++ background, I believe I completely understand how Go handles your type-assertion example here. It can just, as you say, compare Eface._type with type.uint32.

But I cannot fathom how the following code works!

type Craft interface { Float() }

type Car struct {}
type Boat struct {}

func (c Boat) Drive() {}
func (c Boat) Float() {}
func (c Car) Drive() {}

var vface interface { Drive() }
var cface interface { Float() }

func main() {
  b := Boat{}
  vface = b
  cface = vface.(Craft)
}

On the line cface = vface.(Craft), all the compiler knows about vface is that it is some kind of Vehicle. (Incidentally, its _type compares equal to type.Boat; but nobody said anything about Boat on this line, so that can't be relevant to anything.) So how does the runtime know how to convert an arbitrary Vehicle into a Craft?

If you initialize b := Car{} instead, then you get panic: interface conversion: main.Car is not main.Craft: missing method Float at runtime — which is exactly what I would expect — but, I don't understand how the runtime figured that out. Does the _type structure for Car contain a list of the names and signatures of every one of its methods, and then the interface-conversion code walks through that whole list at runtime to collect the needed methods to populate a Craft itab (or panic)?

In C++ terms, your Eface.(uint32) example is simply a std::any_cast — relatively cheap — but the vface.(Craft) example seems much wilder, much more dynamic and costly — so costly that it can't be done at all in C++. Is that right?