Reputation: 17
Why doesn't Go think it's type mismatch error when a struct pointer be assigned to an interface?
package main
import "fmt"
type ABC interface {
a() string
b() int
}
type XYZ struct {
aa string
bb int
}
func (xyz XYZ) a() string {
return "XYZ"
}
func (xyz XYZ) b() int {
return 123
}
func main() {
var xyz *XYZ
var abc ABC = xyz // type of abc is *main.XYZ,I think that Golang can find this error here, but why not?
fmt.Printf("%T\n", abc)
a, ret := abc.(*XYZ)
fmt.Println(a, ret) // type of a is *main.XYZ
fmt.Println(a.a()) // will occur a error, because the type of a(*main.XYZ) not implements the interface ABC
}
I want know why Go doesn't think this is an error at "var abc ABC = xyz"
Upvotes: 0
Views: 219
Reputation: 17
@icza, maybe I don't understand all of what you said, but I think I can give a answer for the question now. when I invoke a method on a value(not pointer, the method of a struct that implements a interface, and the receiver of the method is a pointer). the Golang will create a new object and copy value from the original struct value, then, the iface.data will point the new object, now ,when we pass the pointer of the new object to method, it will can be modify, but this operate will not change the origin struct value, this is not any useful, so, Golang will occur a error when we assign a struct value to a interface(pointer receiver)
Upvotes: 0
Reputation: 31691
XYZ does implement ABC. This has to do with how method sets are determined (emphasis added):
A type may have a method set associated with it. The method set of an interface type is its interface. The method set of any other type T consists of all methods declared with receiver type T. The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T).
The method set determines whether an interface is implemented:
An interface type specifies a method set called its interface. A variable of interface type can store a value of any type with a method set that is any superset of the interface. Such a type is said to implement the interface.
When calling *XYZ.a()
, the Go compiler can always automatically dereference the pointer to obtain a value receiver. There is no downside to doing so, because the receiver cannot be modified (as far as the caller is concerned).
The inverse is true if and only if the value is addressable:
type T struct {}
func (*T) M()
func main() {
var t T
t.M() // ok; t is addressable and the compiler rewrites this to (*t).M()
var m map[string]T
m["x"].M() // error: cannot take the address of m["x"]
}
See also: Golang Method Sets (Pointer vs Value Receiver)
Upvotes: 1
Reputation: 417522
will occur a error, because the type of a(*main.XYZ) not implements the interface ABC
Wrong. *main.XYZ
implements ABC
(else abc = xyz
would fail at compile-time, try to rename the method b
to c
for example), but the variable a
holds a nil
pointer (of type *XYZ
). And since the XYZ.a()
method has value receiver, to call that, a pointer value of type *XYZ
would have to be dereferenced. But a nil
pointer points to nothing, it cannot be dereferenced, an attempt to do so results in a runtime panic, just as you experienced.
If you would initialize xyz
in the beginning with a non-nil
pointer, it would work:
var xyz *XYZ = new(XYZ)
Try it on the Go Playground.
Also note that if XYZ.a()
and XYZ.b()
would have pointer receivers, then it would also work if xyz
is nil
:
func (xyz *XYZ) a() string {
return "XYZ"
}
func (xyz *XYZ) b() int {
return 123
}
func main() {
var xyz *XYZ
// ...
Try it on the Go Playground. The reason for this is because if the receivers are pointers, the nil
pointer does not have to be dereferenced in order to call methods with pointer receivers, so no runtime panic occurs. Of course if in the methods you would refer to the XZY.aa
or XYZ.bb
fields, that would be a runtime panic, but your current method implementations does not do so, so it works.
Upvotes: 0