CodeBreaker
CodeBreaker

Reputation: 564

How to set tags while using reflect.New

When creating a new struct from existing struct, tags are not set on the new struct.

For example:

package main

import (
    "fmt"
    "reflect"
)

type Foo struct {
  Bar string `custom:"tag"`
}

func readTag(e interface{}) {
  t := reflect.TypeOf(e).Elem()
  f, _ := t.FieldByName("Bar")
  fmt.Println(f.Tag)
}

func main() {
  foo := &Foo{"baz"}

  fmt.Println(foo)
  readTag(foo)

  fooType := reflect.TypeOf(foo).Elem()
  newFoo := reflect.New(fooType).Elem()
  newFoo.FieldByName("Bar").SetString("baz2")
  fmt.Println(newFoo)
  readTag(&newFoo)// empty
}

Playground link: https://play.golang.org/p/7-zMPnwQ8Vo

How to set tags while using reflect.New? Is it even possible?

Upvotes: 1

Views: 2007

Answers (1)

icza
icza

Reputation: 418247

Tags do not belong to instances, tags belong to types.

So when you create new instances of your type, their type will be the same "wearing" the same tags. It doesn't matter if you create new instances with literals or via the reflect package.

The problem in your case is that newFoo is of type reflect.Value, and &newFoo is of type *reflect.Value, it's not a pointer to your struct (not of type *Foo).

If you unwrap the struct value:

newFoo.Interface()

And you pass this, and you make the Elem() call optional (only do it if it's a pointer):

func readTag(e interface{}) {
    t := reflect.TypeOf(e)
    if t.Kind() == reflect.Ptr {
        t = t.Elem()
    }
    f, _ := t.FieldByName("Bar")
    fmt.Println(f.Tag)
}

Then you'll get the same tag (try it on the Go Playground):

&{baz}
custom:"tag"
{baz2}
custom:"tag"

You get the same result if you keep the reflect.Value wrapping the struct pointer, and unwrap from it:

newFooPtr := reflect.New(fooType)
newFoo := newFooPtr.Elem()
newFoo.FieldByName("Bar").SetString("baz2")
fmt.Println(newFoo)
readTag(newFooPtr.Interface()) // empty

Then readTag() doesn't need to be modified. Try this version on the Go Playground.

Upvotes: 4

Related Questions