88

What is the way of printing "Foo" here? In this example, what prints is "string".

http://play.golang.org/p/ZnK6PRwEPp

type A struct {
    Foo string
}

func (a *A) PrintFoo() {
    fmt.Println("Foo value is " + a.Foo)
}

func main() {
    a := &A{Foo: "afoo"}
    val := reflect.Indirect(reflect.ValueOf(a))
    fmt.Println(val.Field(0).Type().Name())
}
sat
  • 5,489
  • 10
  • 63
  • 81

7 Answers7

93

You want val.Type().Field(0).Name. The Field method on reflect.Type will return a struct describing that field, which includes the name, among other information.

There is no way to retrieve the field name for a reflect.Value representing a particular field value, since that is a property of the containing struct.

James Henstridge
  • 42,244
  • 6
  • 132
  • 114
  • "There is no way to retrieve the field name for a reflect.Value representing a particular field value" - Why is that the case? Isn't the type and name part of the field itself? – sat Jun 21 '14 at 00:57
  • @sat As soon as you have the `reflect.Value` of a specific field, it is no different to any other variable. Only the struct has the information about it's fields. – nemo Jun 21 '14 at 01:06
  • 1
    @sat: in your example, `val.Field(0)` is no different to a `reflect.Value` for any other string. It doesn't "remember" that it was part of a struct. – James Henstridge Jun 21 '14 at 01:10
  • @JamesHenstridge - But I don't really need it to remember that it belongs to a struct. All I was trying to print was the name of the variable from the field itself using reflection (whether its part of a struct or not). Sorry if its really a newbie question – sat Jun 21 '14 at 01:30
  • That's the thing: the variable doesn't have a name -- its just a location in memory. – James Henstridge Jun 21 '14 at 01:34
  • So the struct maintains meta information for the name? But a simple variable has none? – sat Jun 21 '14 at 01:42
  • The runtime type information for struct types includes the names and types of its members, yes. – James Henstridge Jun 21 '14 at 01:49
  • Also note that there is no in-memory pointer from the variable `a` to the type `*A`: that is static information. The way the reflect package works is that calling `reflect.ValueOf(a)` involves creating an interface variable holding the `a`. This variable will use `a`'s static type, and that is what is used to do the introspection. – James Henstridge Jun 21 '14 at 01:54
40

I think the better way to get the fields' name in the struct is

func main() {
    a := &A{Foo: "afoo"}
    val := reflect.ValueOf(a).Elem()
    for i:=0; i<val.NumField();i++{
        fmt.Println(val.Type().Field(i).Name)
    }
}

There are two tips:

  1. use .Elem() after you reflect.ValueOf(a), because in your case, a is a pointer.
  2. val.Field(i).Type().Name is totally different from val.Type().Field(i).Name. The latter one can get the name of the field in the struct

Hope that it is helpful..

If you want to have a look at more cases, please check my 2mins article

oscarz
  • 1,184
  • 11
  • 19
32

You need to Get the Field of the Type Definition not of the Value.

http://play.golang.org/p/7Bc7MJikbJ

package main

import "fmt"
import "reflect"

type A struct {
    Foo string
}

func (a *A) PrintFoo() {
    fmt.Println("Foo value is " + a.Foo)
}

func main() {
    a := &A{Foo: "afoo"}
    val := reflect.Indirect(reflect.ValueOf(a))
    fmt.Println(val.Type().Field(0).Name)
}
fabrizioM
  • 46,639
  • 15
  • 102
  • 119
29

Note that this solution references a Go module which is now deprecated, and is no longer being maintained. It was deprecated as of Oct 11, 2018.

With the new Names method of the structs package it's even easier:

package main

import (
    "fmt"

    "github.com/fatih/structs"
)

type A struct {
    Foo string
    Bar int
}

func main() {
    names := structs.Names(&A{})
    fmt.Println(names) // ["Foo", "Bar"]
}
Liam Stanley
  • 1,961
  • 1
  • 11
  • 11
Fatih Arslan
  • 16,499
  • 9
  • 54
  • 55
4
package main

import "fmt"
import "reflect"

type A struct {
    Foo string
}

func (a *A) PrintFoo() {
    fmt.Println("Foo value is " + a.Foo)
}

func main() {
    a := &A{Foo: "afoo"}

    //long and bored code
    t := reflect.TypeOf(*a)
    if t.Kind() == reflect.Struct {
        for i := 0; i < t.NumField(); i++ {
            fmt.Println(t.Field(i).Name)
        }
    } else {
        fmt.Println("not a stuct")
    }

    //shorthanded call
    fmt.Println(reflect.TypeOf(*a).Field(0).Name)//can panic if no field exists

}
3

You can use this function, which takes the struct as the first parameter, and then its fields. It returns the map type, which is convenient to use

If you use fields from another structure, nothing will happen

If you try to use a different type, it will cause panic

Note that the field has an ordinal number according to the list (starting from 0). All fields in the structure must start with uppercase

func GetStructFieldName(Struct interface{}, StructField ...interface{}) (fields map[int]string) {
    fields = make(map[int]string)
    s := reflect.ValueOf(Struct).Elem()

    for r := range StructField {
        f := reflect.ValueOf(StructField[r]).Elem()

        for i := 0; i < s.NumField(); i++ {
            valueField := s.Field(i)
            if valueField.Addr().Interface() == f.Addr().Interface() {
                fields[i] = s.Type().Field(i).Name
            }
        }
    }
    return fields
}

Full example and playground

package main

import (
    "fmt"
    "reflect"
)

type Example struct {
    Apple bool
    Pear  int
}

func GetStructFieldName(Struct interface{}, StructField ...interface{}) (fields map[int]string) {
    fields = make(map[int]string)

    for r := range StructField {
        s := reflect.ValueOf(Struct).Elem()
        f := reflect.ValueOf(StructField[r]).Elem()

        for i := 0; i < s.NumField(); i++ {
            valueField := s.Field(i)
            if valueField.Addr().Interface() == f.Addr().Interface() {
                fields[i] = s.Type().Field(i).Name
            }
        }
    }
    return fields
}

func main() {
    e := Example{}

    names := GetStructFieldName(&e, &e.Apple, &e.Pear)

    fmt.Println(names)
    fmt.Println(names[0], names[1])

    for i := range names {
        fmt.Println(names[i])
    }

    /* Output:
    map[0:Apple 1:Pear]
    Apple Pear
    Apple
    Pear
    */
}
Aiv Aki
  • 47
  • 5
  • 1
    Some day I should really start with throwing money at people who write answers like you do. This is exactly what I was looking for! – Anticro Aug 24 '23 at 12:58
2

You can also use https://github.com/fatih/structs

// Convert the fields of a struct to a []*Field
fields := s.Fields()

for _, f := range fields {
    fmt.Printf("field name: %+v\n", f.Name())
}
Thellimist
  • 3,757
  • 5
  • 31
  • 49