3

I'm trying to understand what I'm doing wrong here.. all responses appreciated :)

If uncomment "// grow()" works,

else errors:

prog.go:38:2: impossible type switch case: p (type plant) cannot have dynamic type plant1 (grow method has pointer receiver) prog.go:39:16: impossible type assertion: plant1 does not implement plant (grow method has pointer receiver) prog.go:40:2: impossible type switch case: p (type plant) cannot have dynamic type plant2 (grow method has pointer receiver) prog.go:41:16: impossible type assertion: plant2 does not implement plant (grow method has pointer receiver) prog.go:60:12: cannot use p1 (type plant1) as type plant in argument to showHeight: plant1 does not implement plant (grow method has pointer receiver) prog.go:61:12: cannot use p2 (type plant2) as type plant in argument to showHeight: plant2 does not implement plant (grow method has pointer receiver)

https://play.golang.org/p/oMv7LdW85yK

package main

import (
    "fmt"
)

type plant1 struct {
    name   string
    height int
}

type plant2 struct {
    species string
    height  int
}

func (self *plant1) grow() {
    self.height++
}
func (self *plant2) grow() {
    self.height++
}

func (self plant1) getHeight() int {
    return self.height
}
func (self plant2) getHeight() int {
    return self.height
}

type plant interface {
    getHeight() int
    //grow()
}

func showHeight(p plant) {
    switch p.(type) {
    case plant1:
        fmt.Println(p.(plant1).name, `Height = `, p.(plant1).getHeight())
    case plant2:
        fmt.Println(p.(plant2).species, `Height = `, p.(plant2).getHeight())
    }

}

func main() {

    p1 := plant1{
        name:   `Plant 10`,
        height: 1,
    }
    p2 := plant2{
        species:   `Plant 20`,
        height: 1,
    }
    p1.grow()
    p1.grow()
    p2.grow()   

    showHeight(p1)
    showHeight(p2)
}
jellycat
  • 57
  • 2
  • 7
    You defined the grow method on a pointer type, ie `*plant1` not `plant1`. When `p` is the plant interface and you do a type switch on it the switch cases can contain only types that satisfy that interface, ie `*plant1` not `plant1`. – mkopriva Dec 19 '18 at 06:56
  • 2
    https://play.golang.org/p/r8Bo92OhMCd – mkopriva Dec 19 '18 at 06:58
  • 2
    Note that, when doing a type switch, if you store the type asserted value in a variable then inside the block scope of each case the variable will have the type of that case. And therefore you do not have to do type assertion again inside the cases. ie https://play.golang.org/p/gkSRe9n264w – mkopriva Dec 19 '18 at 07:02
  • 2
    Aside: you should generally not mix pointer and non-pointer receivers in methods for a particular type. Choose one and use that for all methods to avoid confusion. https://golang.org/doc/faq#methods_on_values_or_pointers. Here, `getHeight` should have a pointer receiver. – Peter Dec 19 '18 at 08:00
  • Also note that it's considered bad practice in Go to name method receivers with generic names like "self" or "this". Read any Go code in the standard library, the community, or the [Tour of Go](https://tour.golang.org/methods/1) for examples on idiomatic Go code. – Adrian Dec 19 '18 at 14:24

3 Answers3

2

The value you wrap into interface isn't addressable. The value have to be addressable to call methods on pointer receivers. The grow methods are declared with pointer receiver. So the compiler sees that plant interface is being implemented neither by plant1 type nor by plant2 type. Thus you can't pass plant1 or plant2 as plant to showHeight func. And the switch is impossible because implementations of plant interface don't include plant1 and plant2 types.

See Why value stored in an interface is not addressable in Golang

Sergej Zagursky
  • 453
  • 4
  • 16
0

Your Function func showHeight(p plant) accepts an interface as parameter, and an interface only accepts the types you implement.

Case commented //grow()

Since you have commented grow() plant has only one method getHeight() which you have implemented for plant 1 and plant 2 and it all goes smooth.

Case uncommented grow()

Now none of your types(plant1 or plant 2) implement plant interface because grow() has been defined on *plant1 and *plant2 not on plant1 and plant2. Result; Your function showHeight(p plant) which accepts interface type plant, cant accept pl,p2 because these are not of type plant anymore.

Fix :

Pass pointers to function as showHeight(&p1) and showHeight(&p2) and in function definition's cases and print statements, replace plant1 and plant2 with *plant1 and *plant2

Confusion which can follow is , getHeight has not been implemented on *plant1 and *plant2, why do *plant1 and *plant2 are of type plant? Logic: When you pass pointers, values can be derived from these so as to call getHeight, but when you pass values, no address can be derived to call grow()

SIDENOTE: p.(*plant2).getHeight() can be changed to p.getHeight() as p is interface and it can call its own methods.

topenion
  • 390
  • 3
  • 12
0

This due to the way the Method Set in Go works. In Go, a Method on a concrete receiver type implements both the value type and pointer type, while a method on a pointer receiver type implements only the pointer value.

In the case of the grow method, the pointer receiver type *plant1/*plant2 are used, so only a pointer value of those types can implement the grow method on an interface and the concrete value type can't.

Since the receiver type on getHeight method is a concrete receiver type, it implements both the pointer type value (*plant1/*plant2) and the type value of both plant & plant2.

If you change the parameter(p) passed to showHeight to a *plant1/*plant2 type and change the type assertion to expect *plant1/*plant2 this would solve the error.

check this code example playground link

Kayslay
  • 111
  • 5