Problem calling methods on struct assigned to interfaces

Hello there, i’v a problem calling a method on a struct assigned to an interface
this is my code

package main

import "fmt"

type Dog struct {}
type Cat struct {}
type Duck struct {}

func (dog *Dog) Woof() {
  fmt.Println("woof")
}
func (cat *Cat) Meow() {
  fmt.Println("woof")
}
func (human *Duck) Quack() {
  fmt.Println("quack")
}

func main() {
  band := make(map[string]interface{})
  band["dog"] = &Dog{}
  band["cat"] = &Cat{}
  band["duck"] = &Duck{}

  for name, animal := range band {
    if name == "dog" {
      animal.Woof()
    }
    if name == "cat" {
      animal.Meow()
    }
    if name == "duck" {
      animal.Quack()
    }
  }
}

And the error i get is:

./test.go:27: animal.Woof undefined (type interface {} is interface with no methods)
./test.go:30: animal.Meow undefined (type interface {} is interface with no methods)
./test.go:33: animal.Quack undefined (type interface {} is interface with no methods)

What i’m doing wrong?

When you added things to band their types became interface{}. Since interface{} does not have Woof, etc. the compiler did not know how to call them. There are two ways to get from an interface to the underlying types: type assertions and type switches. In this case type switches do what you want:

package main

import "fmt"

type Dog struct{}
type Cat struct{}
type Duck struct{}

func (dog *Dog) Woof() {
	fmt.Println("woof")
}
func (cat *Cat) Meow() {
	fmt.Println("meow")
}
func (human *Duck) Quack() {
	fmt.Println("quack")
}

func main() {
	band := make(map[string]interface{})
	band["dog"] = &Dog{}
	band["cat"] = &Cat{}
	band["duck"] = &Duck{}

	for _, animal := range band {
		switch a := animal.(type) {
		case *Dog:
			a.Woof()
		case *Cat:
			a.Meow()
		case *Duck:
			a.Quack()
		default:
			panic(fmt.Sprintf("unhandled type %T", animal))
		}
	}
}

I also found another bug. :cat:s meow, they don’t woof. I fixed it while implementing the type switch.

1 Like

this makes everything more clear, ty

Hi,

I’m new to programming in go (and programming in general), but why wouldn’t you implement this simply using simply a ‘talk’ interface? Or is this just proof of concept?

All best,
D

Edit: Is this relevant?

If you think about what you could do with a type switch like this you will see that what is being implemented in this example is fairly unimportant. You could use one switch to do completely different operations for each type. This is beyond just the behavior of speaking, each of those case statements could call an entire block of statements/functions bound up with those interfaces.

Essentially you could have this switch say process a number of different types to perform whatever you need them to do, they need not be necessarily of the same behavior. If you are dealing with types that do implement the same behavior then your observation would be best.

Hope that helps.

Use a type switch when you want to do different things with different things.

Use an interface (e.g., talk) when you want to do the same thing with different things.

1 Like

Thanks all, that makes sense. I’ve only just wrapped my head around the point of interfaces - so this is another level of meta up :slight_smile:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.