I am 2-days old in the world of Golang, and going through the go tour. I couldn't help but notice a peculiarity which I cannot seem to be able to come at terms with a proper reasoning.
This code is running perfectly:
package main
import (
"fmt"
"math"
)
type Vertex struct{
X,Y float64
}
type Abser interface{
Abs() float64
}
func (v Vertex) Abs() float64{ //method with value receiver argument
return math.Sqrt(v.X*v.X+v.Y*v.Y)
}
func main(){
var myVer Vertex = Vertex{3,4}
var inter Abser
inter = &myVer //assigning *Vertex type to inter
fmt.Println(inter.Abs())
}
Meanwhile, the following code shows an error:
package main
import (
"fmt"
"math"
)
type Vertex struct{
X,Y float64
}
type Abser interface{
Abs() float64
}
func (v *Vertex) Abs() float64{ //method with pointer receiver argument
return math.Sqrt(v.X*v.X+v.Y*v.Y)
}
func main(){
var myVer Vertex = Vertex{3,4}
var inter Abser
inter = myVer //assigning Vertex type to inter
fmt.Println(inter.Abs())
}
The error is:
interface.go:18: cannot use myVer (type Vertex) as type Abser in assignment: Vertex does not implement Abser (Abs method has pointer receiver)
Until reaching this section of the tour, I could understand that the creators of Go have let-go of the cumbersome notations like
(*v).method1name()
(&v).method2name()
So that methods with value receivers can be used with both values and pointers, and vice versa.
Why does the language discriminate between the two (value and pointer) when working with interfaces? Wouldn't it be more convenient if the same reference/dereference principles could apply here?
I hope that I am not missing something too apparent. Thanks!
This distinction arises because if an interface value contains a pointer *T, a method call can obtain a value by dereferencing the pointer, but if an interface value contains a value T, there is no safe way for a method call to obtain a pointer.
Go's interface values are really a pair of pointers. When you put a concrete value into an interface value, one pointer starts pointing at the value. The second will now point to the implementation of the interface for the type of the concrete value.
To create interface use interface keyword, followed by curly braces containing a list of method names, along with any parameters or return values the methods are expected to have.
The method set is the collection of functions associated with that type as methods and used by the Go compiler to determine whether some type can be assigned to a variable with an interface type.
"Intro++ to Go Interfaces" illustrates the issue:
*Vertex
is a type. It’s the “pointer to aVertex
” type. It’s a distinct type from (non-pointer)Vertex
. The part about it being a pointer is part of its type.
You need consistency of type.
"Methods, Interfaces and Embedded Types in Go":
The rules for determining interface compliance are based on the receiver for those methods and how the interface call is being made.
Here are the rules in the spec for how the compiler determines if the value or pointer for our type implements the interface:
*T
is the set of all methods with receiver *T
or T
This rule is stating that if the interface variable we are using to call a particular interface method contains a pointer, then methods with receivers based on both values and pointers will satisfy the interface.
T
consists of all methods with receiver type T
.This rule is stating that if the interface variable we are using to call a particular interface method contains a value, then only methods with receivers based on values will satisfy the interface.
Karrot Kake's answer about method set is detailed also in go wiki:
The method set of an interface type is its interface.
The concrete value stored in an interface is not addressable, in the same way that a
map
element is not addressable.
Therefore, when you call a method on an interface, it must either have an identical receiver type or it must be directly discernible from the concrete type.Pointer- and value-receiver methods can be called with pointers and values respectively, as you would expect.
Value-receiver methods can be called with pointer values because they can be dereferenced first.
Pointer-receiver methods cannot be called with values, however, because the value stored inside an interface has no address.
("has no address" means actually that it is not addressable)
The method set for a pointer type includes the value receiver methods, but the method set for a value does not include the pointer receiver methods. The method set for a value type does not include the pointer receiver methods because values stored in an interface are not addressable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With