Why implicit non-pointer methods not satisfy interface?
Asked Answered
B

4

5

Assuming we have an understanding that,

For explicit method definition for type X, GO compiler implicitly defines the same method for type *X and vice versa, if I declare,

func (c Cat) foo(){
  //do stuff_
} 

and declare,

func (c *Cat) foo(){
  // do stuff_
}

then GO compiler gives error,

Compile error: method re-declared

which indicates that, pointer method is implicitly defined and vice versa


In the below code,

package main

type X interface{
  foo();
  bar();
}

type Cat struct{

}

func (c Cat) foo(){
  // do stuff_
}

func (c *Cat) bar(){
  // do stuff_
}

func main() {
  var c Cat
  var p *Cat
  var x X

  x = p // OK; *Cat has explicit method bar() and implicit method foo()
  x = c //compile error: Cat has explicit method foo() and implicit method bar()

}

GO compiler gives error,

cannot use c (type Cat) as type X in assignment:
    Cat does not implement X (bar method has pointer receiver)

at x = c, because, implicit pointer methods satisfy interfaces, but implicit non-pointer methods do not.

Question:

Why implicit non-pointer methods do not satisfy interfaces?

Bulgar answered 29/1, 2017 at 14:49 Comment(7)
@KevinWallis I had to make an another change. so Edited on top of yours. You can take control nowBulgar
its fine now :)Methedrine
Possible duplicate of Go, X does not implement Y (... method has a pointer receiver). Also related: Types of Go struct methods that satisfy an interface.Seagraves
@Seagraves In that query assignment fails due to not implementing interface, here x = p does not failBulgar
You are confusing people with imaginary implicit method definitions.Salpingotomy
@Bulgar Yes, and if you read the linked answer, you'll know why.Seagraves
Also also related: Golang Method Sets (Pointer vs Value Receiver).Kiloton
S
6

Let's look into the language specification:

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).

In your example, the method set of the interface type x is [foo(), bar()]. The method set of the type Cat is [foo()], and the method set of the type *Cat is [foo()] + [bar()] = [foo(), bar()].

This explains, why variable p satisfies the interface x, but variable c doesn't.

Salpingotomy answered 29/1, 2017 at 15:28 Comment(7)
c.bar() works as shown in this code. So, how would you prove that The method set of the type(Cat) is [foo()]?Bulgar
c.bar() works, because the compiler replaces this statement with (&c).bar(). But, c cannot be assigned to the interface x, because for it's own type Cat, there is no such method as bar().Salpingotomy
For non-pointer, var c Cat, how would you, invoke (*c).bar()?Bulgar
It was a typo, fixed.Salpingotomy
As per the spec, What is the method set of type(*Cat) in this code? and why p.bar() failed? as per your point, Why (*p).bar() is not invoked in place of p.bar()?Bulgar
p.bar() fails because p is nil. You didn't assign it a value.Salpingotomy
p.foo() tries to dereference a pointer that has no value (in order to pass it to the method as a receiver) and fails. p.bar() passes the nil to the method successfully. But, if you would try to use the receiver inside of the method, you will fail. You are off the topic already, and are probably out of coffee too :)Salpingotomy
J
1

Method set

Following the spec:

The method set of any other named 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).

Method set definition sounds weird until you follow addressable and not addressable types concept.

Addressable and not addressable types

It is possible to call a pointer receiver method on a value if the value is of addressable type.

As with selectors, a reference to a non-interface method with a value receiver using a pointer will automatically dereference that pointer: pt.Mv is equivalent to (*pt).Mv.

As with method calls, a reference to a non-interface method with a pointer receiver using an addressable value will automatically take the address of that value: t.Mp is equivalent to (&t).Mp.

It is ok to call pointer receiver methods on values till you are dealing with addressable types (struct is addressable):

type Cat struct {}

func (c *Cat) bar() string { return "Mew" }

func main() {
    var c Cat
    c.bar()
}

Variables of interface type are not addressable

But not all Go types are addressable. Also variables referenced through interfaces are not addressable.

It is impossible to call pointer receiver on values of not addressable types:

type X interface {
    bar() string
}

type Cat struct{}

func (c *Cat) bar() string { return "Mew" }

/* Note `cat` variable is not a `struct` type value but
   it is type of `X` interface therefor it is not addressable. */
func CatBar(cat X) { 
    fmt.Print(cat.bar())
}

func main() {
    var c Cat
    CatBar(c)
}

So with the following error Go runtime prevents segment fault:

cannot use c (type Cat) as type X in assignment: Cat does not implement X (bar method has pointer receiver)

Jabe answered 29/1, 2017 at 15:37 Comment(5)
@overexchange, t is not implicit definition it is just the fact that pointer has access (actually it is access) to value but value has no access to pointer. Value receiver restricted by that.Jabe
1) also contains the method set of T, mean, If receiver type(*Cat) has defined method (bar()) then compiler creates implicit definition of method bar() for receiver type(Cat), which I say in first section of query as well as in the problem line in the code(with comments). 2) With you line x = &c, you are saying that, &c is of type *Cat and *Cat type has satisfied interface, which I say also in the query, so it worksBulgar
@overexchange, "t" is "it" which lost "i" =)Jabe
Please go thru this code and check(c.bar()) at line 24 and explain me, who defined method bar() for type(Cat)?Bulgar
@Bulgar no one. There is no method bar() for type Cat. But because c is addressable, the compiler converts the call c.bar() to (&c).bar() for your convenience. Likewise if p is a *Cat, the compiler converts p.foo() to (*p).foo() for you. There are no implicit methods, only implicit rewriting of calls.Kiloton
U
0

Add a little to dev.bmax's answer.

type Cat struct{
}

func (c Cat) foo(){
  // do stuff_
}

func (c *Cat) bar(){
  // do stuff_
}

you can do

var c cat
c.bar() // ok to call bar(), since c is a variable.

but not

cat{}.bar() // not ok to call bar(), c is not a variable.

It's legal to call a *T method on an argument of type T so long as the argument is a variable; the compiler implicitly takes its address. But this is mere syntactic sugar: a value of type T does not posses all methods that a *T pointer does, and as a result it might satisfy fewer interfaces.

On the other hand, you can always call foo() with Cat or *Cat.

Uganda answered 29/1, 2017 at 15:40 Comment(0)
A
0

How about this?

package main

import (
    "fmt"
)

type Growler interface{
    Growl() bool
}

type Cat struct{
    Name string
    Age int
} 

// *Cat is good for both objects and "references" (pointers to objects)
func (c *Cat) Speak() bool{
    fmt.Println("Meow!")
        return true
}

func (c *Cat) Growl() bool{
    fmt.Println("Grrr!")
    return true
}

func main() {
    var felix Cat // is not a pointer
    felix.Speak() // works :-)
    felix.Growl() // works :-)

    var ginger *Cat = new(Cat) 
    ginger.Speak() // works :-)
    ginger.Growl() // works :-)
}
Adowa answered 29/1, 2017 at 16:59 Comment(3)
felix.Speak() and felix.Growl() works because as I said in Query, for explicit method definition(Speak() & Browl()) for type(*Cat), GO compiler implicitly defines those two methods(Speak() & Browl()) for type(Cat). Do you agree?Bulgar
So far as I can see, all the rules are succinctly explained under "Method Expressions" and "Method Values" in the Go language specification. I was just trying to provide a quick example of how that works for felines! ;-)Adowa
Is the interface "Growler" used here anywhere?Flosser

© 2022 - 2024 — McMap. All rights reserved.