英文:
embedded interface in a struct behaves in a very weird manner
问题
我发现很难理解Go语言的内部工作原理。有些情况下,它会表现出奇怪的行为。
type TestInterface interface {
Walk()
}
type A struct {
}
func (a *A) Walk() {
fmt.Println("hello world")
}
type B struct {
TestInterface
}
func main() {
var a *A
b := B{}
a.Walk() // 即使a是nil,这里不会引发panic
b.Walk() // 这里会引发panic
}
由于b
嵌入了TestInterface
,所以b.Walk()
会以类似a.Walk()
的方式在结构体A
上调用Walk
方法。
为什么其中一个能正常工作,而另一个会引发panic呢?
英文:
I find hard to understand how Go works internally. There are some cases where it exhibits weird behavior.
type TestInterface interface {
Walk()
}
type A struct {
}
func (a *A) Walk() {
fmt.Println("hello world")
}
type B struct {
TestInterface
}
func main() {
var a *A
b := B{}
a.Walk() // This will not panic even though a is nil
b.Walk() // This will panic.
}
Since b
embeds TestInterface
, b.Walk()
will internally call Walk
method on struct A
in a similar way as a.Walk()
is called.
Why is it then that one works and the other panics?
答案1
得分: 2
根据Go文档关于Method_declarations的说明,方法与接收器的基本类型绑定。
接收器是通过在方法名之前指定的额外参数部分来指定的。该参数部分必须声明一个非可变参数,即接收器。其类型必须是定义的类型T或定义的类型T的指针。T被称为接收器的基本类型。接收器的基本类型不能是指针或接口类型,并且它必须在与方法相同的包中定义。该方法被称为绑定到其接收器的基本类型,并且方法名仅在类型T或*T的选择器中可见。
因此,你的方法绑定到main.A类型,而var a
的类型也是main.A。因此,可以调用Walk()
方法。但是,如果在方法内部将a
用作值,它也会引发panic。
在B
类型的b
中,TestInterface
的默认类型为nil,这就是为什么会引发panic的原因。你必须将实现接口的类型注入到B
中,以避免panic。
func main() {
var a *A
b := B{TestInterface:a}
fmt.Println(reflect.TypeOf(a), b)
a.Walk() // 即使a为nil,这不会引发panic
b.Walk() // 现在这不会再引发panic。
}
在playground中运行
英文:
As Go doc mentioned about Method_declarations, Methods are bound to the base type of the receiver.
> The receiver is specified via an extra parameter section preceding the
> method name. That parameter section must declare a single non-variadic
> parameter, the receiver. Its type must be a defined type T or a
> pointer to a defined type T. T is called the receiver base type. A
> receiver base type cannot be a pointer or interface type and it must
> be defined in the same package as the method. The method is said to be
> bound to its receiver base type and the method name is visible only
> within selectors for type T or *T.
So your Methods are bound to *main.A type and type of var a
also *main.A. So it is allowed to call Walk()
method. But if you use a
as a value inside the method, it will also panic.
Inside your b
of B
type, TestInterface
's default type is nil and that's why It is panicing. You have to inject interface implemented type into B
to avoid panicing.
func main() {
var a *A
b := B{TestInterface:a}
fmt.Println(reflect.TypeOf(a), b)
a.Walk() // This will not panic even though a is nil
b.Walk() // This will not panic anymore.
}
run in playground
答案2
得分: 0
感谢@mkopriva上面的评论,我按照以下方式修改了程序,现在它是有意义的。这也意味着在Go语言中,nil具有与之关联的类型。
type TestInterface interface {
Walk()
}
type A struct {
}
func (a *A) Walk() {
fmt.Println("hello world")
}
type B struct {
TestInterface
}
func main() {
var a *A
b := B{TestInterface: a}
// 两者都不会引发恐慌
a.Walk()
// 由于a是nil,TestInterface仍然是nil。
b.Walk()
}
英文:
Thanks to @mkopriva comment above, I modified the program in the following way and it makes sense now. It also means that nil in Go has a type associated with it.
type TestInterface interface {
Walk()
}
type A struct {
}
func (a *A) Walk() {
fmt.Println("hello world")
}
type B struct {
TestInterface
}
func main() {
var a *A
b := B{TestInterface: a}
// both will not panic
a.Walk()
// TestInterface is still nil as a is nil.
b.Walk()
}```
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论