I'm working with a library that implements a type which accepts handlers and calls them for you when it should. I'd like to create a supertype that embeds the type and has properties beyond what the embedded type does. And I'd like to be able to use these properties in a handler.
Using my type as an argument fails the type checking, using the base type in handler decl works, but then I cant access the new fields. I am new to go, & would love to know how to do this, or what to suggest in changing the library to enable (Interfaces instead of the handler decl?)...
Contrived example:
package main
type Animal struct {
Color string
feeders map[string]feeder
}
type feeder func(*Animal, string) string
func (a *Animal) addFeeder(name string, fn feeder) {
a.feeders[name] = fn
}
type mamal struct {
Animal
hair string
}
func feedHuman(m *mamal, food string) string {
return "you got " + m.Color + " " + m.hair + " hair in your " + food
}
func main() {
a := mamal{Animal{Color: "red"}, "bushy"}
a.addFeeder("man", feedHuman)
// fails to compile feedHuman needs to take *Animal but then cant access hair"
}
You are right, you cannot do that because a mamal is not an Animal. There is no inheritance in go so this is not a solution. But you can achieve this behaviour with interfaces. By the way, your code does not work because you didn't initialize the feeders map, so you will get a problem after compilation.
Solution 1: Use type assertion
package main
import "fmt"
type Animal struct {
Color string
feeders map[string]feeder
}
type feeder func(interface{}, string) string
func (a *Animal) addFeeder(name string, fn feeder) {
a.feeders[name] = fn
}
type mamal struct {
Animal
hair string
}
func feedHuman(i interface{}, food string) string {
m := i.(mamal)
return "you got " + m.Color + " " + m.hair + " hair in your " + food
}
func main() {
a := mamal{Animal{feeders: make(map[string]feeder), Color: "red"}, "bushy"}
a.addFeeder("man", feedHuman)
fmt.Println(a.feeders["man"](a, "pineapple"))
// => you got red bushy hair in your pineapple
}
This is a bit dangerous because if the interface you pass is not a mamal, it will panic.
Second solution: Use a Feeder interface
package main
import "fmt"
type Feeder interface {
Feed(string) string
}
type Animal struct {
Color string
feeders map[string]Feeder
}
func (a *Animal) addFeeder(name string, feeder Feeder) {
a.feeders[name] = feeder
}
type mamal struct {
Animal
hair string
}
func (m *mamal) Feed(food string) string {
return "you got " + m.Color + " " + m.hair + " hair in your " + food
}
func main() {
a := &mamal{Animal{Color: "red", feeders: make(map[string]Feeder)}, "bushy"}
a.addFeeder("man", a)
fmt.Println(a.feeders["man"].Feed("pineapple"))
// you got red bushy hair in your pineapple
}
Though it works just as fine, it limits the number of handlers to one, so I do not think this is what you seek.