gogo-reflect

How create type using reflect having pointer receivers


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


Solution

  • 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")
    }