Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go reflect field index - single index vs. slice

Tags:

reflection

go

reflect.StructField has an Index field that is typed []int. The documentation on this is slightly confounding:

    Index     []int     // index sequence for Type.FieldByIndex

And of course Type.FieldByIndex follows suit as expected, with the somewhat clearer explanation of its behavior:

    // FieldByIndex returns the nested field corresponding
    // to the index sequence.  It is equivalent to calling Field
    // successively for each index i.
    // It panics if the type's Kind is not Struct.
    FieldByIndex(index []int) StructField

But, there is also Type.Field():

    // Field returns a struct type's i'th field.
    // It panics if the type's Kind is not Struct.
    // It panics if i is not in the range [0, NumField()).
    Field(i int) StructFiel

So the behavior of those respectively is very clear.

My question: Exactly for which fields / what circumstances will a reflect.StructField have an Index with len(field.Index) > 1? Is this there to support enumerating embedded fields (reachable through an anonymous field in the parent)? Does it happen in other cases? (ie. is it safe to assume if !field.Anonymous, then we can just use field.Index[0] as an argument to Field(i int)?)

like image 918
BadZen Avatar asked Oct 23 '25 13:10

BadZen


2 Answers

It can refer to fields in embedded or non embedded structs, recursively:

type Foo struct {
    Bar string
}

type Baz struct {
    Zoo Foo
}

func main() {

    b := Baz{Zoo:Foo{"foo"}}
    v := reflect.ValueOf(b)

    fmt.Println(v.FieldByIndex([]int{0})) //output: <main.Foo Value>

    fmt.Println(v.FieldByIndex([]int{0, 0})) //output: foo

}
like image 86
Not_a_Golfer Avatar answered Oct 25 '25 11:10

Not_a_Golfer


So I was looking for the answer to this question, and I really haven't been able to find anything. To explain why the answer above is not satisfactory, I have an example:

package main

import (
    "fmt"
    "reflect"
)

type (
    A struct {
        W int
        X int
    }
    B struct {
        Y int
        A A
    }
    C struct {
        B B
        Z int
    }
)

func main() {
    b := B{1, A{2, 3}}
    c := C{b, 4}

    bt := reflect.TypeOf(b)
    ct := reflect.TypeOf(c)

    ba := bt.FieldByIndex([]int{1, 0})
    ca := ct.FieldByIndex([]int{0, 1, 0})

    fmt.Println("B > A = ", ba.Index)
    fmt.Println("C > B > A = ", ca.Index)
}

The output is:

B > A = [0]
C > B > A = [0]

So with the description of StructField.Index as is given in the docs (

Index []int // index sequence for Type.FieldByIndex

) one would assume the output would correspond in some way to retrieving the same field via the FieldByIndex method, and since that is designed for nested fields, the output above might be confusing. If the Index is always an []int of length 1, why even use an array? Why does it not store a single int if it is only in relation to its immediate parent?

The answer is probably simpler than we (those of us who found this confusing) expected. The Index value must often be used as an argument for FieldByIndex, and so it is stored in an array simply for convenience.

like image 21
calvinsomething Avatar answered Oct 25 '25 11:10

calvinsomething



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!