I've two Task struct, namely ShellTask
and CmdTask
. Created a TaskExecutor
Interface and implemented the methods both in ShellTask
and CmdTask
Pointer receiver. And creating the Task Executor dynamically using reflection at runtime. However when I run the program at runtime getting error with message panic: interface conversion: main.ShellTask is not main.TaskExecutor: missing method Run
, However if I change pointer receiver to non-pointer receiver things start working.
How to create the object with pointer, so the pointer receiver works.
package main
import (
"errors"
"fmt"
"reflect"
)
type Task struct {
Name string
}
type TaskExecutor interface {
Run() error
}
type ShellTask struct {
*Task
}
func (t *ShellTask) Run() error {
fmt.Println("Running linux task")
return nil
}
type CmdTask struct {
*Task
}
func (t *CmdTask) Run() error {
fmt.Println("Running linux task")
return nil
}
var registry = make(map[string]reflect.Type)
func Register(moduleName string, v interface{}) {
registry[moduleName] = reflect.TypeOf(v)
}
func GetTaskExecutor(name string) (TaskExecutor, error) {
if k, ok := registry[name]; ok {
newPtr := reflect.New(k)
e := newPtr.Elem()
f := e.Interface().(TaskExecutor)
return f, nil
}
return nil, errors.New("no task handler found")
}
func main() {
Register("cmd", CmdTask{})
Register("shell", ShellTask{})
exec, err := GetTaskExecutor("shell")
if err != nil {
panic(err)
}
exec.Run()
}
Go Playground link
https://pkg.go.dev/reflect@go1.21.0#Value.Elem
Elem returns the value that the interface v contains or that the pointer v points to. It panics if v's Kind is not Interface or Pointer. It returns the zero Value if v is nil.
So, if you want the pointer and not the pointed-to value, you need to remove the e := newPtr.Elem()
step.
func GetTaskExecutor(name string) (TaskExecutor, error) {
if k, ok := registry[name]; ok {
p := reflect.New(k)
f := p.Interface().(TaskExecutor)
return f, nil
}
return nil, errors.New("no task handler found")
}