4

The structure is like:

type Auth_msg struct {
    Msg_class       [2]byte
    Msg_content_pty [2]byte

I am fresh to use Reflect in Go and I encounter this:

panic: reflect: call of reflect.Value.Bytes on array Value

This occurs when I run val.Field(i).Bytes(), however the when I try to print it: fmt.PrintLn(val.Field(i)), it prints out the right arrays.

I just wonder, how I can retrieve the Msg_class in an array or a slice?

peterh
  • 11,875
  • 18
  • 85
  • 108
Steve Yang
  • 37
  • 1
  • 4
  • 1
    val.Field(i).Index(j).Uint() – Volker Jan 29 '21 at 07:49
  • Is it compulsory to traversal through the array's elements? – Steve Yang Jan 29 '21 at 07:55
  • "compulsory" No, you always can do ugly unsafe tricks. Given that we talk about 2 (in words: two) elements here there is no _reason_ not to access these two array elements one by the other. – Volker Jan 29 '21 at 08:36
  • It seems some misunderstanding. `val.Field(i)` represents the ith field in struct `Auth_msg`, like `Msg_class`. I am confused how I can retrieve it as an array. If `Msg_class` is a slice, the expression is obviously `val.Field(i).Bytes()`. But now it's an array, so I wonder a `val.Field(i).Bytes()`-like-expression? – Steve Yang Jan 29 '21 at 08:54
  • Your very first answer helps me out, but I just wonder why there is no simple method for array fields like `val.Field(i).Bytes()` – Steve Yang Jan 29 '21 at 08:56
  • 1
    No there is not. Arrays are pretty uncommon in Go and package reflect does not provide as much support for arrays as it does for slices (especially byte slices). Note that you always can do val.Field(i).Interface().([2]byte). Whether that is clearer, safer, faster is something you have to decide. – Volker Jan 29 '21 at 09:14
  • 1
    Thank you so much for your detailed elaboration. I get what you mean. – Steve Yang Jan 29 '21 at 09:19

1 Answers1

1

In Go, there is a distinction between an array and a slice. Value.Bytes() explicitly works only for a byte slice (link to docs).
note : I don't know why it doesn't handle byte arrays ; it probably was written that way, and it makes the implementation of reflect.Bytes() simpler. Anyway : slices are definitely the common use case in Go, and it is easy to convert an array to a slice :


You can create a slice pointing to the array using [:] :

    v := reflect.ValueOf(msg.Msg_class)
    fmt.Println("kind :", v.Kind()) // prints 'array'
    // fmt.Printf("bytes : % x\n", v.Bytes())  // panics

    v = reflect.ValueOf(msg.Msg_class[:])
    fmt.Println("kind :", v.Kind())        // prints 'slice'
    fmt.Printf("bytes : % x\n", v.Bytes()) // works

https://play.golang.org/p/sKcGaru4rOq


To turn an array into a slice using reflect, you can call .Slice() on a reflect.Value.

One constraint, mentioned in the doc, is that the array value must be addressable.
I haven't got all the details sorted out, but one way to make sure the reflect Value is addressable is to call reflect.ValueOf() on a pointer, and then call .Elem() on that pointer value :

var arr [2]byte
arr[0] = 'g'
arr[1] = 'o'

// take ValueOf a *pointer* to your array, and let reflect dereference it :
v := reflect.ValueOf(&arr).Elem()
// this sets the "canAddr" flag on this value
fmt.Println("arr value - CanAddr() :", v.CanAddr()) // prints 'true'
slice := v.Slice(0, v.Len())
fmt.Printf("arr bytes : % x\n", slice.Bytes()) // prints '67 6f'

// for a field inside a struct : take a pointer to the struct
var msg Auth_msg
msg.Msg_class[0] = 'a'
msg.Msg_class[1] = 'z'

v = reflect.ValueOf(&msg).Elem()
fmt.Println("msg value - CanAddr() :", v.CanAddr()) // prints 'true'

// now reflect accepts to call ".Slice()" on one of its fields :
field := v.FieldByName("Msg_class")
slice = field.Slice(0, field.Len())
fmt.Printf("msg.Msg_class bytes : % x\n", slice.Bytes()) // prints '61 7a'

https://play.golang.org/p/SqM7yxl2D96

LeGEC
  • 46,477
  • 5
  • 57
  • 104
  • I see the difference of `slice` and `array`. The case is I am travesalling through the elements in `msg`. So I have to write `val := reflect.TypeOf(msg)`, so `i` here refers to the elements in msg like `Msg_class`. If `Msg_class` is a slice, the expression is obviously `val.Field(i).Bytes`. But now it's an array, so I want a `val.Field(i).Bytes`-like-expression – Steve Yang Jan 29 '21 at 08:17
  • As of go1.20 (at least), .Bytes() works on arrays: "It panics if v's underlying value is not a slice of bytes or an addressable array of bytes. " – oliverpool Jun 22 '23 at 09:41