Given the following Go code:
package main
type CatToy interface {
Rattle() string
}
type Cat struct {
}
func (cat *Cat) Play(catToy CatToy) {
println("The cat is playing!", catToy.Rattle())
}
type DogToy interface {
Roll() string
}
type Dog struct {
}
func (dog *Dog) Play(dogToy DogToy) {
println("The dog is playing!", dogToy.Roll())
}
type SuperToy struct {
}
func (toy *SuperToy) Rattle() string {
return "Rattle!!!"
}
func (toy *SuperToy) Roll() string {
return "Rolling..."
}
type Pet interface {
Play(toy interface{})
}
func main() {
cat := &Cat{}
dog := &Dog{}
superToy := &SuperToy{}
// Working
cat.Play(superToy)
dog.Play(superToy)
// Not Working
pets := []Pet{cat, dog}
for _, pet := range pets {
pet.Play(superToy)
}
}
I am getting these errors:
# command-line-arguments
./main.go:65:16: cannot use cat (type *Cat) as type Pet in array or slice literal:
*Cat does not implement Pet (wrong type for Play method)
have Play(CatToy)
want Play(interface {})
./main.go:65:21: cannot use dog (type *Dog) as type Pet in array or slice literal:
*Dog does not implement Pet (wrong type for Play method)
have Play(DogToy)
want Play(interface {})
The SuperToy
implements both CatToy
and DogToy
. However, when I create an interface Pet
with interface as an argument, I get an error. May I know how I'll be able to get an array/slice with a cat and dog inside? I want to iterate through this slice and call a function for each one. I also want to keep the CatToy
and DogToy
interfaces. I'm also ok with removing the Pet
interface.
More info: In the future, it's more likely that I'll add more pets
. I don't think that I'll add more actions such as Play
.
Thank you
I get what you are trying to do, but it's impossible: your Cat
and Dog
types do not implement the Pet
interface, as their Play
methods take different types, so you won't be able to just call Play
on them with your SuperToy
.
To fix this, you would need to create a Toy
interface that has both the Roll
and the Rattle
methods, and make Pet.Play
, Cat.Play
and Dog.Play
take this interface as an argument.
package main
type Cat struct {
}
func (cat *Cat) Play(catToy Toy) {
println("The cat is playing!", catToy.Rattle())
}
type Dog struct {
}
func (dog *Dog) Play(dogToy Toy) {
println("The dog is playing!", dogToy.Roll())
}
type Toy interface {
Roll() string
Rattle() string
}
type SuperToy struct {
}
func (toy *SuperToy) Rattle() string {
return "Rattle!!!"
}
func (toy *SuperToy) Roll() string {
return "Rolling..."
}
type Pet interface {
Play(toy Toy)
}
func main() {
cat := &Cat{}
dog := &Dog{}
superToy := &SuperToy{}
// Working
cat.Play(superToy)
dog.Play(superToy)
// Not Working
pets := []Pet{cat, dog}
for _, pet := range pets {
pet.Play(superToy)
}
}
Gives the output
The cat is playing! Rattle!!!
The dog is playing! Rolling...
The cat is playing! Rattle!!!
The dog is playing! Rolling...
You could get the Play methods to take an interface{}
and then do a type assertion inside the method:
func (dog *Dog) Play(toy interface{}) {
dogToy, isDogToy := toy.(DogToy)
if !isDogToy {
println("The dog does not know what to do with this toy!")
return
}
println("The dog is playing!", dogToy.Roll())
}
Full executable example on Go Playground:
Here's another working solution:
package main
type CatToy interface {
Rattle() string
}
type Cat struct {
Toy CatToy
}
func (cat *Cat) Play() {
println("The cat is playing!", cat.Toy.Rattle())
}
type DogToy interface {
Roll() string
}
type Dog struct {
Toy DogToy
}
func (dog *Dog) Play() {
println("The dog is playing!", dog.Toy.Roll())
}
type SuperToy struct {
}
func (toy *SuperToy) Rattle() string {
return "Rattle!!!"
}
func (toy *SuperToy) Roll() string {
return "Rolling..."
}
type Pet interface {
Play()
}
func main() {
superToy := &SuperToy{}
cat := &Cat{superToy}
dog := &Dog{superToy}
// Working
cat.Play()
dog.Play()
// Working also
pets := []Pet{cat, dog}
for _, pet := range pets {
pet.Play()
}
}
This solution keeps the Cat + CatToy
and Dog + DogToy
independent from the main + SuperToy
. This allows extraction to separate packages.
However, I do somewhat agree with @Volker and @Ullaakut. This solution seems not idiomatic for Go. I'm still not sure about the proper solution.