About method implementation for an interface - go

Can anyone explain why the calling of a.Abs() works?
In my opinion, 'a' is a variable of type *Vertex, but the type *Vertex doesn't implement the method Abs.
package main
import (
"fmt"
"math"
)
type Abser interface {
Abs() float64
}
func main() {
var a Abser
v := Vertex{3, 4}
a = &v // a *Vertex implements Abser
// In the following line, v is a *Vertex (not Vertex)
// and does NOT implement Abser, but why does this calling work?
fmt.Println(a.Abs())
}
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

From the fine specification:
Method sets
[...] 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). [...]
Your Abs function is in the method sets of both Vertex and *Vertex so *Vertex is an Abser just like Vertex is.
Other related sections:
Method values
Selectors
In general, pointers are automatically dereferenced when possible so you can say x.M() and x.V without worrying about whether or not x is a pointer and there is no need for C's -> or manual dereferencing (i.e. (*x).M() or (*x).V).

Related

How to define interface for methods on struct pointer [duplicate]

This question already has answers here:
X does not implement Y (... method has a pointer receiver)
(4 answers)
Closed 1 year ago.
For structs, it is possible to define a func that can update struct variables. Is there any way to use those functions in interface?
In the following code, I tried to create a minimal example to describe my question. two struct of Rect and Circle are defined. Both of the structs have Perimeter and Expand functions.
Perimeter function calculates the perimeter of the shape. Expand function increases the perimeter of the shape by changing its properties. Also Shape interface with the Perimeter method signature is defined.
viewShapeData function accept input parameter of Shape type. This function view data of the shapes and also run the Perimeter method and view the perimeter of shapes.
package main
import "fmt"
type Shape interface {
Perimeter() float64
}
type Rect struct {
width float64
height float64
}
type Circle struct {
radius float64
}
func (s Rect) Perimeter() float64 {
return 2 * (s.width + s.height)
}
func (s Circle) Perimeter() float64 {
return 2 * 3.14 * s.radius
}
func (s *Rect) Expand(increaseValue float64) {
s.width += increaseValue / 2
}
func (s *Circle) Expand(increaseValue float64) {
s.radius += increaseValue / 3.14 / 2
}
func main() {
a := Circle{radius: 10.0}
b := Rect{width: 2.4, height: 5}
a.Expand(1)
viewShapeData(a)
b.Expand(1)
viewShapeData(b)
}
func viewShapeData(s Shape) {
fmt.Printf("value: %v, Type: %T, Perimeter: %f\n", s, s, s.Perimeter())
}
Now I'm looking for a way to call the Expand method in the viewShapeData function. I tried different ways and applied the following changes in the described code:
type Shape interface {
Perimeter() float64
Expand(float64)
}
func main() {
a := Circle{radius: 10.0}
b := Rect{width: 2.4, height: 5}
viewShapeData(a)
viewShapeData(b)
}
func viewShapeData(s Shape) {
s.Expand(1)
fmt.Printf("value: %v, Type: %T, Perimeter: %f\n", s, s, s.Perimeter())
}
But these errors appear:
cannot use a (type Circle) as type Shape in argument to viewShapeData:
Circle does not implement Shape (Expand method has pointer receiver)
cannot use b (type Rect) as type Shape in argument to viewShapeData:
Rect does not implement Shape (Expand method has pointer receiver)
Please give me a solution or tell me why Golang does not support this kind of coding.
So, if you want to use a pointer receiver, you also should pass a pointer into the function that takes the interface.
For example
func main() {
a := Circle{radius: 10.0}
b := Rect{width: 2.4, height: 5}
a.Expand(1)
viewShapeData(&a)
b.Expand(1)
viewShapeData(&b)
}
https://play.golang.com/p/pmHd5yl_v8p

Switch case on interface type

I'm trying to identify a type that implements an interface using a switch:
package main
import "fmt"
type Abser interface {
Abs() float64
}
type Vertex struct {
X, Y int
}
func (v *Vertex) Abs() float64 {
return float64(v.X)
}
func do(i interface{}) {
switch v := i.(type) {
case Abser:
fmt.Println("theres an Abser")
case Vertex:
fmt.Println("theres a Vertex")
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
func main() {
do(Vertex{1,2})
}
This code outputs the value for Vertex instead of Abser. Ideally it should have output theres an Abser since Vertex implements the Abs() method. Is it possible to use a switch-case to do this?
It is not about the switch command, but about pointer receivers.
If you change func (v *Vertex) Abs() float64 to func (v Vertex) Abs() float64, it will give the output theres an Abser.
In general code, a type can use its pointer method - you can call Vertex{1,2}.Abs(), but what happened behind it is that the Go compiler rewrites it as (&Vertex{1,2}).Abs() for you. So Vertex does not implement Abser.
But on the contrary, a pointer type has all the methods its underlying type has. So even if the API is defined as func (v Vertex) Abs() float64, &Vertex{1,2} is still an Abser. See: https://play.golang.org/p/ONnsjApMywO
See Also:
Method set: https://golang.org/ref/spec#Method_sets
The problem is that Vertex isn't an Abser, only *Vertex is, if you change your Abs() impl to
func (v Vertex) Abs() float64 {
return float64(v.X)
}
then Vertex itself is an Abser and your code will print theres an Abser

Methods with a Pointer Receiver

I am newbie in computer science.I just read the code snippet below from The Go programming language and got the following error log.
func (p *Point) ScaleBy(factor float64){
p.X *= 2
p.Y *= 2
}
Point{1, 2}.ScaleBy(2)
# error log
cannot call pointer method on point literal
cannot take the address of point literal
point literal.scaleby(2) used as value
The book explained that we can not call a *Point method on a non-addressable Point receiver, because there's no way to obtain address of a temporary value.
However, if I print &Point{1, 2}, this would not throw error. Accordingly, why Point{1,2} is a non-addressable Point receiver?
By using Point{1, 2}.ScaleBy(2) you are trying to call pointer receiver method ScaleBy with value: Point{1, 2}:
The method set of any other type T consists of all methods declared
with receiver type T.
but if you use addressable type:
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).
then it is possible: meaning you or the compiler should get the address of temporary value (Taking the address of a composite literal):
Address operators:
For an operand x of type T, the address operation &x generates a
pointer of type *T to x. The operand must be addressable, that is,
either a variable, pointer indirection, or slice indexing operation;
or a field selector of an addressable struct operand; or an array
indexing operation of an addressable array. As an exception to the
addressability requirement, x may also be a (possibly parenthesized)
composite literal. If the evaluation of x would cause a run-time
panic, then the evaluation of &x does too.
ref: https://golang.org/ref/spec#Address_operators
You may call (&Point{1, 2}).ScaleBy(2)
like this working sample code (pointer receiver):
package main
import "fmt"
func main() {
p := (&Point{1, 2}).ScaleBy(2)
fmt.Println(p) // &{2 4}
}
type Point struct {
X, Y int
}
func (p *Point) ScaleBy(factor float64) *Point {
p.X *= 2
p.Y *= 2
return p
}
you may call Point{1, 2}.ScaleBy(2)
like this working sample code (value receiver):
package main
import "fmt"
func main() {
p := Point{1, 2}.ScaleBy(2)
fmt.Println(p) // &{2 4}
}
type Point struct {
X, Y int
}
func (p Point) ScaleBy(factor float64) *Point {
p.X *= 2
p.Y *= 2
return &p
}
output:
&{2 4}
also see this working sample code (pointer receiver):
package main
import "fmt"
func main() {
p := Point{1, 2}
p.ScaleBy(2)
fmt.Println(p) // {2 4}
}
type Point struct {
X, Y int
}
func (p *Point) ScaleBy(factor float64) {
p.X *= 2
p.Y *= 2
}
output:
{2 4}
When you write Point{1,2} you simply declaring and initializing a value of type Point. If you don't assign it to a variable, it is discarded.
Go disallows this behavior of calling a pointer method on a simple value since a pointer method states an intent of object (pointed to by the pointer) modification. A pointer method called with a value would be useless in most of the cases since a value is passed by copy to the method. Any modifications made to the value will be done to that copied value and no actual modification would occur.
If you tried this, it would work:
type Point struct {
x, y int
}
func (p Point) X() {
fmt.Println(p.x)
}
Point{1, 2}.X() // 1
You can read more about it here: https://golang.org/doc/effective_go.html#pointers_vs_values

Implementing Mixins and an Inconsistency in Compiler Behavior

Mixins can be implemented in Go (1.4.1) using embedding and since struct{} occupies no memory (as I understand) it fits for the situations that we want to add some functionality or just add a method to a type that may actually has nothing to do with it's state, but we like to avoid ParseThing(...) and instead write thing.Parse(...).
So having:
type X struct{}
func (x X) F() {
fmt.Println("functionality in X.F()")
}
type Y struct{ X }
type Z struct{ Y }
Then if we do:
var z Z
z.F()
Will give us:
functionality in X.F()
So far so good.
Now let's add another type OX with method F() and embed it in Z:
type Z struct {
Y
OX
}
type OX struct{} // overriding X
func (x OX) F() {
fmt.Println("functionality in OX.F()")
}
Interesting! Now we get functionality in OX.F() which shows us that Go compiler searches for the method, starting from type it self and then the last embedded type. We can check that by adding F() to Z:
func (x Z) F() {
fmt.Println("functionality in Z.F()")
}
The output is functionality in Z.F(). Now if we remove the Z.F() method and add F() to Y:
//func (x Z) F() {
// fmt.Println("functionality in Z.F()")
//}
func (x Y) F() {
fmt.Println("functionality in Y.F()")
}
Then we see this error ambiguous selector z.F; redirecting via pointers makes no difference.
Question 1: Why that's so?
The extra level of indirection Y meant for something else, but brought me to this. And as I've guessed func (t T) String() string{} is an exception. This code:
type X struct{}
func (x X) String() string {
return "in X.String()"
}
type Y struct{ X }
type Z struct {
Y
OX
}
type OX struct{} // overriding X
func (x OX) String() string {
return "in OX.String()"
}
func (x Y) String() string {
return "in Y.String()"
}
And then this:
var z Z
fmt.Println(z)
Gives us:
{in Y.String() in OX.String()}
Which is logical. But if we use pointer receivers:
import (
"fmt"
"testing"
)
func TestIt(t *testing.T) {
var z Z
fmt.Println(z)
}
type X struct{}
func (x *X) String() string {
return "in X.String()"
}
type Y struct{ X }
type Z struct {
Y
OX
}
type OX struct{} // overriding X
func (x *OX) String() string {
return "in OX.String()"
}
func (x *Y) String() string {
return "in Y.String()"
}
Will print out:
{{{}} {}}
Question 2: Why is that so?
Question 1
The compiler is correct. How should it decide, which of OX.F and Y.F should it use? It can't. So it's up to you to call the desired method directly: either with
z.Y.F()
or
z.OX.F()
Edit: As for why your example worked until you've defined F on Y, this is mentioned in the Spec:
For a value x of type T or *T where T is not a pointer or interface type, x.f denotes the field or method at the shallowest depth in T where there is such an f. If there is not exactly one f with shallowest depth, the selector expression is illegal.
(Emphasis added.)
Before you defined the method, the shallowest implementation was OX.F. After you've defined Y.F, there became two Fs on the same level, which is illegal.
Question 2
Again, the compiler is correct. You have embedded types Y and OX into Z, not *Y and *OX. As written in the Spec,
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).
*T has all methods of T, but not the other way around. Methods sets of OX and Y are empty, so obviously, fmt.Println just prints them as if they were any other kind of struct with no String() method defined.
Ainar-G write neat answer
Spec:
For a value x of type T or *T where T is not a pointer or interface
type, x.f denotes the field or method at the shallowest depth in T
where there is such an f. If there is not exactly one f with
shallowest depth, the selector expression is illegal.
I'd like to add a bit
Spec:
If S contains an anonymous field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T.
So things would work if you just use referencing to promote methods like
fmt.Println(&z)
But this will cause ambiguity in selection cause of there are few possibilities for String method and so selector String is illegal due to spec. Compiler must complain, but it doesn't. This behaviour looks unspecified and can be only explained as special case for common printing operation to my mind.
This will work as expected
var y Y
fmt.Println(&y)
Here is working example
Playground

Method receivers ambiguity

Working on the go tour today. I noticed that I could pass struct literals to methods associated with pointer to structs, and vice versa. Why is this allowed?
package main
import (
"fmt"
)
type Vertex struct {
X, Y float64
}
func (v Vertex) Scale (f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func (v *Vertex) ScaleP(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := &Vertex{3, 4}
vLiteral := Vertex{3, 4}
v.Scale(5)
fmt.Println(v)
v.ScaleP(5)
fmt.Println(v)
vLiteral.Scale(5)
fmt.Println(vLiteral)
vLiteral.ScaleP(5)
fmt.Println(vLiteral)
}
Output:
&{3 4}
&{15 20}
{3 4}
{15 20}
See Method sets:
A type may have a method set associated with it (§Interface types, §Method declarations). The method set of an interface type is its interface. The method set of any other type T consists of all methods with receiver type T. The method set of the corresponding pointer type *T is the set of all methods with receiver *T or T (that is, it also contains the method set of T). Further rules apply to structs containing anonymous fields, as described in the section on struct types. Any other type has an empty method set. In a method set, each method must have a unique method name.
The method set of a type determines the interfaces that the type implements and the methods that can be called using a receiver of that type.
EDIT:
See also Calls:
A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m():

Resources