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