你使用的Go版本是什么(go version
)?
go version go1.12 linux/amd64
你做了什么?
package main
import (
"fmt"
)
type S struct {}
func (S) M(n int) {
fmt.Println(n)
}
type T struct {
*S
}
type I interface {
M(n int)
}
func foo() {
var v = &T{}
var i I = v
f := i.M // ok
v.S = &S{}
f(111) // 111
}
func bar() {
var v = &T{}
f := v.M // panic
v.S = &S{}
f(111)
}
func main() {
foo()
bar()
}
你觉得会看到什么?
相同的行为
你看到了什么?
不同的行为。
8条答案
按热度按时间ahy6op9u1#
CC @griesemer
zvokhttg2#
我认为这个行为在规范中已经明确指定:
与选择器一样,对非接口方法的值接收器的指针引用会自动解引用该指针:pt.Mv等同于(*pt).Mv。
与方法调用一样,对非接口方法的指针接收器的可寻址值的引用会自动获取该值的地址:t.Mp等同于(&t).Mp。
此外,#47863 添加了一个关于如何评估接收者的例子,我们现在可以关闭这个问题吗?cc @griesemer@mdempsky@ianlancetaylor
wmvff8tz3#
这是我在玩这个#32021时发现的另一个bug,它已经关闭了。但我认为反射和直接代码之间也存在不一致性。
yquaqz184#
/cc @ianlancetaylor
wixjitnu5#
在
f := v.M
的情况下,我们在创建方法值的点处检查一个nil
指针。我们这样做是因为在方法值被调用时检查nil
指针会导致令人困惑的回溯。在f := i.M
的情况下,我们检查i
不是nil
,但我们不检查该值本身是否包含一个nil
指针。所以这个工作是按照预期进行的,为了避免在生成的 Package 函数中发生
nil
解引用,但是,你是对的,它并不完全一致。xcitsw886#
我会说这种不一致需要更好地记录下来,因为这会让使用Go的人感到惊讶。请将此作为一个文档问题。
svmlkihl7#
在我看来,#32021的不一致性比当前问题更严重。我认为,如果可能的话,反射代码实现和直接代码实现应该相互一致。
就个人而言,我更倾向于当前的反射实现方式。
slhcrj9b8#
如果我们记录某事,那将是对语言规范的更改,说明你不能创建一个
nil
指针的方法值,也不能在指针为nil
时创建一个提升方法的方法值。换句话说,在创建方法值时进行所有nil
检查,而不是在调用时进行。对于接口类型,如果接口值是
nil
,则不能创建值方法。然而,我们不检查接口值中存储的任何内容,我不确定我们是否可以这样做。