3

Go 使用动态和静态绑定。据我了解,如果您需要使用类型断言,那么它是动态的。我想验证我的假设。

type Xer interface { 
  X()
}

type XYer interface {
  Xer
  Y()
}

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

假设:

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)

因此,从type A->转换时interface B,如果A满足,B则不需要断言,并且可以在编译时生成 itable。如果您在不需要的地方使用断言,情况会怎样:

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

在这种情况下会发生什么?如果有人可以为我澄清这一点,那就太好了。

4

2 回答 2

5

为了扩展 jnml 的答案,6g仍然会生成一个类型断言。

empty := x.(interface{})

扩展为:

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)

为了澄清这里发生了什么,在第 34 行中,InterfaceTypeofinterface{}被加载到堆栈的第一个值。第 35-36 和 37-38 行将选项卡和数据值放入x 堆栈。然后堆栈准备好调用runtime.assertI2E,它只是将基础类型和数据分配给返回值。编译器知道您分配给一个空接口,因此调用assertI2E:I2E代表接口到Eface(空接口),因此不需要检查方法。唯一的限制assertI2E是断言的值必须是一个接口。

但是,如果您正在执行x.(Xer),runtime.assertI2I将被调用,然后检查方法是否实现了接口。

于 2013-04-11T15:41:53.370 回答
5

我不知道什么是接口的静态绑定或接口的动态绑定。语言规范从未提及此类术语。让我假设您的意思是编译时的类型检查和运行时的类型检查。有了这个假设,你所有的例子,A​​FAICS,都是正确的。它归结为一个简单的模式(相关语言规范部分的浓缩版本):

  • 所有的赋值都在编译时进行类型检查。
  • 所有类型断言 ( .(T)) 在运行时都经过类型检查。

这也回答了“在这种情况下会发生什么?” .

也就是说,编译器可以自由优化可以在编译时证明类型已知且分配兼容的情况。但这只是一个不能依赖的实现细节——因为它可能不是其他符合规范的实现的情况。

实际上 gc 编译器具有(IINM)这样的优化,甚至在相反的意义上。它可以说“不可能的类型断言”,它可以在编译时证明类型断言将失败。

于 2013-04-11T15:31:36.580 回答