Go 允许一些选择器的简化形式。
例如,在下面这个程序中,t1.M1是(*t1).M1的简化形式,而t2.M2则是(&t2).M2的简化形式。
在编译时,编译器将把简化的形式正规化为它们原来各自的完整形式。
下面这个程序打印出0和9,因为对t1.X的修改对(*t1).M1的估值结果没有影响。
package main
type T struct {
X int
}
func (t T) M1() int {
return t.X
}
func (t *T) M2() int {
return t.X
}
func main() {
var t1 = new(T)
var f1 = t1.M1 // (*t1).M1
t1.X = 9
println(f1()) // 0
var t2 T
var f2 = t2.M2 // (&t2).M2
t2.X = 9
println(f2()) // 9
}
在下面的代码中,函数foo运行正常,但函数bar会产生恐慌。
原因是s.M是(*s.T).M的简化形式。
在编译时,编译器会将此简化形式规范化为原来的完整形式。
在运行时,如果s.T是 nil接收器,那么对*s.T的估值将导致一个恐慌。
对s.T的两次修改对*s.T的估值结果没有影响。
package main
type T struct {
X int
}
func (t T) M() int {
return t.X
}
type S struct {
*T
}
func foo() {
var s = S{T: new(T)}
var f = s.M // (*s.T).M
s.T = nil
f()
}
func bar() {
var s S
var f = s.M // panic
s.T = new(T)
f()
}
func main() {
foo()
bar()
}
请注意,接口方法值和通过反射得到的方法值将被延迟扩展为提升的方法值。
例如,在下面的程序中,对s.T.X的修改对通过反射和接口方式得到的方法值的返回值有影响。
package main
import "reflect"
type T struct {
X int
}
func (t T) M() int {
return t.X
}
type S struct {
*T
}
func main() {
var s = S{T: new(T)}
var f = s.M // (*s.T).M
var g = reflect.ValueOf(&s).Elem().
MethodByName("M").
Interface().(func() int)
var h = interface{M() int}(s).M
s.T.X = 3
println( f() ) // 0
println( g() ) // 3
println( h() ) // 3
}
来源:https://github.com/golang/go/issues/47863
但是,在当前版本(1.18 版本)的官方标准 Go 编译器的实现中存在一个 bug。
官方标准 Go 编译器中一个优化会将一些接口方法值过度去虚拟化(de-virtualization)接收器,从而导致不正确的结果。
比如,下面这个程序应该打印出2 2,但是目前它却打印出1 2。
package main
type I interface{ M() }
type T struct{
x int
}
func (t T) M() {
println(t.x)
}
func main() {
var t = &T{x: 1}
var i I = t
var f = i.M
defer f() // 2(正确)
// i.M 将在编译时刻被(错误地)去虚拟化为 (*t).M。
defer i.M() // 1(错误)
t.x = 2
}
目前尚不清楚此 bug 何时将被修复。
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,一年会员只需98元,全站资源免费下载 点击查看详情
本站公众号:巨课网创 内容很干,欢迎关注!
站 长 微 信: lieying62 声明:1、本内容转载于网络,版权归原作者所有!2、本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。3、本内容若侵犯到你的版权利益,请联系我们,会尽快给予删除处理!