Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go interfaces: static vs dynamic binding

Go uses both dynamic and static binding. From my understanding, if you need to use a type assertion then it's dynamic. I want to validate my assumptions.

type Xer interface { 
  X()
}

type XYer interface {
  Xer
  Y()
}

type Foo struct{}
func (Foo) X() { println("Foo#X()") }
func (Foo) Y() { println("Foo#Y()") }

Assumptions:

foo := Foo{}

// static: Foo -> XYer
var xy XYer = foo

// static: XYer -> Xer
var x Xer = xy

// static: Xer -> interface{}
var empty interface{} = x

// dynamic: interface{} -> XYer
xy2 := empty.(XYer)

// dynamic: XYer -> Foo
foo2 := xy2.(Foo)

So when converting from type A -> interface B, if A satisfies B then you don't need an assertion and the itable can be generated at compile time. What about a case where you use an assertion where it's not needed:

var x Xer = Foo{}
empty := x.(interface{})

what happens in this case? If someone could clarify this for me that would great.

like image 309
Ilia Choly Avatar asked Oct 24 '25 08:10

Ilia Choly


1 Answers

To extend on jnml's answer, 6g generates a type assertion nevertheless.

empty := x.(interface{})

is expanded to:

0034 (dumb.go:19) MOVQ    $type.interface {}+0(SB),(SP)
0035 (dumb.go:19) LEAQ    8(SP),BX
0036 (dumb.go:19) MOVQ    x+-32(SP),BP
0037 (dumb.go:19) MOVQ    BP,(BX)
0038 (dumb.go:19) MOVQ    x+-24(SP),BP
0039 (dumb.go:19) MOVQ    BP,8(BX)
0040 (dumb.go:19) CALL    ,runtime.assertI2E+0(SB)
0041 (dumb.go:19) MOVQ    24(SP),BX
0042 (dumb.go:19) MOVQ    BX,empty+-16(SP)
0043 (dumb.go:19) MOVQ    32(SP),BX
0044 (dumb.go:19) MOVQ    BX,empty+-8(SP)

To clarify what is happening here, in line 34 the InterfaceType of interface{} is loaded to the first value of the stack. Line 35-36 and 37-38 put the tab and data values of x onto the stack. The stack is then ready for runtime.assertI2E to be called, which simply assigns the underlying type and data to the return value. The compiler knows that you're assigning to an empty interface, hence the call to assertI2E: I2E stands for Interface to Eface (Empty Interface), so no check for methods is necessary. The only restriction assertI2E enforces is that the asserted value must be an interface.

If however, you're doing x.(Xer), runtime.assertI2I would've been called, which then checks if the methods implement the interface.

like image 64
nemo Avatar answered Oct 25 '25 23:10

nemo