Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

panic: reflect: call of reflect.Value.Call on zero Value

Tags:

reflection

go

I'm trying to use reflection in order to make a dynamic function call based on user input. I'm collecting user input like such:

func main() {
    for {
        reader := bufio.NewReader(os.Stdin)
        fmt.Print("> ")
        command, _ := reader.ReadString('\n')
        runCommandFromString(command)
    }
}

This is how the command is parsed out

func stringCommandParser(cmd string) *Command {
    cmdParts := strings.Split(
        strings.TrimSpace(cmd),
        " ",
    )
    return &Command{
        Name: cmdParts[0],
        Args: cmdParts[1:],
    }
}

func runCommandFromString(cmd string) {
    command := stringCommandParser(cmd)

    c := &Commander{}
    f := reflect.ValueOf(&c).MethodByName(command.Name)
    inputs := []reflect.Value{reflect.ValueOf(command)}
    f.Call(inputs)
}

The commands.go file looks like this

type Command struct {
    Name string
    Args []string
}

type Commander struct {}

func (c Commander) Hello(cmd Command) {
    fmt.Println("Meow", cmd.Args)
}

When I run the program, I get a prompt and I run a command called "Hello" with a param called "world". I would expect "Meow [World]" or something like that to appear. Like this:

> Hello world
Meow world

Instead what I get is a panic that looks like this:

> Hello world
panic: reflect: call of reflect.Value.Call on zero Value

goroutine 1 [running]:
panic(0x123360, 0xc420014360)
    /Users/parris/.gvm/gos/go1.7.4/src/runtime/panic.go:500 +0x1a1
reflect.flag.mustBe(0x0, 0x13)
    /Users/parris/.gvm/gos/go1.7.4/src/reflect/value.go:201 +0xae
reflect.Value.Call(0x0, 0x0, 0x0, 0xc420055e00, 0x1, 0x1, 0x0, 0x0, 0x0)
    /Users/parris/.gvm/gos/go1.7.4/src/reflect/value.go:300 +0x38
main.runCommandFromString(0xc42000c8a0, 0xc)
    /Users/parris/Projects/distro/main/utils.go:26 +0x1a0
main.main()
    /Users/parris/Projects/distro/main/main.go:14 +0x149
exit status 2

How can I fix this? Also, what is happening?

like image 947
Parris Avatar asked Feb 02 '17 05:02

Parris


3 Answers

You might want to check what reflect.ValueOf returns before using it.

In this case you're getting a nil value because reflect can not see functions that aren't exported. hello starts with a lower case letter and therefore isn't exported.

Also, you're making too many pointers here, I suspect that you need to do reflect.ValueOf(c). You're doing and debugging too many things at the same time. Make a simple working example and continue debugging from there.

This works for me: https://play.golang.org/p/Yd-WDRzura

like image 103
Art Avatar answered Oct 16 '22 22:10

Art


Just do two changes in your code. First change reflect.ValueOf(&c) to reflect.ValueOf(c) Secondly change reflect.ValueOf(command) to reflect.ValueOf(*command)

This is the code workingcode

like image 43
legacy_shield Avatar answered Oct 16 '22 23:10

legacy_shield


Call Kind() to check

type Header struct {
    Token string
}
    
type Request struct {
    Header 
}   


func requestedToken(request interface{}) string {
    requestValue := reflect.ValueOf(request)
    header := requestValue.FieldByName("Header12")
    if header.Kind() == 0 {
        return "" //Return here
    }
    token := header.FieldByName("Token")
    if token.Kind() == 0 {
        return ""
    }
    return token.String()
}
like image 44
Châu Văn Khánh Nhật Avatar answered Oct 16 '22 22:10

Châu Văn Khánh Nhật