Get pointer to struct field (reflection)

4,688 views
Skip to first unread message

Guillermo Estrada

unread,
Aug 25, 2015, 1:21:38 AM8/25/15
to golang-nuts
Hey Gophers! I'm trying to generate flags based on struct fields via reflection and I reached an issue with Type Assertion and pointers to struct value, I hope someone can point me in the right direction. This is to do some kind of Task manager library to make my life easier writing binaries.

Example: (note this is a short example without Kind() checks and without PkgPath check for only Exported fields)

type Test struct {
  Name   string    `cmd:"Usage message"`
}
// This will be the tested Method for the command line
func (t Test) Hello() {
 fmt.Println("Hello", t.Name)
}


func GenerateFlags(cmd interface{}) error {

  cmdType := reflect.TypeOf(cmd)
    // Validate Kind() is reflection.Struct and proceed
  for i := 0; i < cmdType.NumField(); i++ {
    field := cmdType.Field(i) // Get the StructField

    // Validate field.PkgPath == "" so we know its an Exported field
    cmdValue := reflect.ValueOf(cmd)    // In case we need to use the reflection.Value
    value := cmdValue.FieldByName(field.Name).Interface()   // get the interface to work with
    switch value.(type) {
      //  Switch cases for each of supported flags values
      case string:
       // Below the type assertion as we give the value of the flag is OK, my problem is trying to get a pointer of the valid type via reflection
flag.StringVar(/*HERE IS MY ISSUE*/,field.Name, value.(string) , field.Tag.Get("cmd"))
     }
  }
}

func main() {
    GenerateFlags(Test{"World"}) // Default value for the flag...
    flag.Parse()
    //More code to execute the method given on the Args...
    // something like $program test:hello -name "Peter"
}

I've tried every way I have found in the docs to 1) Get the pointer and 2) Make the Type assertion to *string, apparently I can do either or, but not both. There must be an easy syntax problem I do not know about. 

Hope someone can help.
Kudos!

Dave Cheney

unread,
Aug 25, 2015, 1:52:28 AM8/25/15
to golang-nuts
Please try

switch v := v.(type) {

case string:
     // v is a string in this scope
default:
    // v is some unknown type
}


Thanks

Dave

Guillermo Estrada

unread,
Aug 25, 2015, 2:06:08 AM8/25/15
to golang-nuts


On Tuesday, August 25, 2015 at 12:52:28 AM UTC-5, Dave Cheney wrote:
Please try

switch v := v.(type) {

case string:
     // v is a string in this scope
default:
    // v is some unknown type
}

Thanks Dave, that works! Doing the type assertion at the switch resolves the issue I had, but does the type asserted value after the assigment points to the original struct field? (or is it a copy in case of ints and such) I mean sending the address to the flag function will be able to change its content after flag.Parse(), correct?

value := cmdValue.FieldByName(field.Name).Interface()
switch value := value.(type) {
case string:
flag.StringVar(&value, strings.ToLower(field.Name), value, field.Tag.Get("cmd"))

}

Carlos Castillo

unread,
Aug 25, 2015, 11:32:33 AM8/25/15
to golang-nuts
No, the value you generated will not be a pointer to the original struct field, and therefore you will not be able to change your original struct. You will need to through reflection get the address of the field (ie: field.Addr()), but to do that, the field, and the struct must be addressable, which requires you pass a pointer to the struct to your reflection function. http://blog.golang.org/laws-of-reflection

Guillermo Estrada

unread,
Aug 27, 2015, 10:25:12 AM8/27/15
to golang-nuts
No, the value you generated will not be a pointer to the original struct field, and therefore you will not be able to change your original struct. You will need to through reflection get the address of the field (ie: field.Addr()), but to do that, the field, and the struct must be addressable, which requires you pass a pointer to the struct to your reflection function. http://blog.golang.org/laws-of-reflection


Gracias Carlos! Passing the struct pointer and then getting the element works as expected in your example, swicthing the type assertion on the pointer is also a nice way to go. 

Matt Joiner

unread,
Jun 9, 2018, 12:00:27 AM6/9/18
to golang-nuts
I implemented just this for flag parsing here: https://github.com/anacrolix/tagflag.
Reply all
Reply to author
Forward
0 new messages