So, you have started to poke around in Go a bit and you are now starting to wonder what sort of metaprogramming options you have at your disposal. Well one option you have, that is in most programming languages, is using reflection. One usage of reflection in Go is working with struct field tags.

What is a struct field tag?

If you have ever written a RESTful JSON api in Go, you probably already know, or at least you have seen them. This is first time most Gophers run into these field tags.

In the example above you will notice that I declared two structs that both have a field Bar of type string. The main difference I am trying to point out is that the second struct, FooWithTag, also declares a field tag. This tag is used by the json package when marshaling and unmarshaling data. If you pay close attention to the output you will notice Bar is upper-cased in the first output and lower-cased in the second. In this instance, the field tag is telling the json package to write the key as bar not Bar. If I would have declared my tag to be:

type FooWithTag struct {

Bar string `json:"tomato"`

}

It would have printed:

{"tomato":"something"}

Now that we have seen how the json package makes use of field tags let’s explore how we can create our own.

Defining our own custom tag

Let’s start by playing a little fast and loose. When one behaves is such a way they often forget their manners. So let’s make a struct field tag to make our code a little extra polite.

type Foo struct {

Bar string `manners`

}

Every time we see a field with the tag manners we should post-fix the string with pretty please. The snippet below outlines what we are trying to accomplish.

In order to enable this behavior we need to lean on our good friend the reflect package.

Reflection

Reflection is the way we can, at runtime, figure out all of the tags a struct holds. Let’s first just print out all of the tags we see in our struct.

Now we need to hone in on the tag we actually want to work with. To get at a particular tag the reflect package provides a Lookup method.

Notice there is no output.

🤔 Why not?

Because we were playing too fast and loose 😱. Let’s take a look at some documentation from the reflect package.

By convention, tag strings are a concatenation of optionally space-separated key:”value” pairs. Each key is a non-empty string consisting of non-control characters other than space (U+0020 ‘ ‘), quote (U+0022 ‘“‘), and colon (U+003A ‘:’). Each value is quoted using U+0022 ‘“‘ characters and Go string literal syntax.

Now that we have read the docs we know we need to change how our struct field tag was defined. Once we know the field is properly marked with the tag we are looking for, we need to figure out a way to access the value of that field. We can accomplish this with the reflect package as well. After making all of these edits the code should look something like this…

Taking it one step further

Great. We defined our custom tag, got it to do what we want, and now it is all good right? Not exactly. You see, anytime you are using the reflect package you are opening yourself up to panics. To be safe, we should be checking the Kind of fields we are operating on. For this tag, let’s keep it simple and say it should only work with fields of type string. Also, since we now know we can associate a value with our struct field tag, how about we say the tag can optionally provide a number that defines how many times the word pretty is printed.

🎉Congratulations🎉, we have now created a tag that makes our code a little more polite!

What I made

You now know everything you need to know to venture out into the wild and start creating your own custom tags. If you would like to reference an example that is a little more complex I encourage you to take a look verify. It is a package I threw together that mimics some of the stuff you might find in javax.validation.constraints if you are familiar. There are other more complete packages in the Go community that do the same thing as the one I created, but I wanted to build something small and real to help myself understand tags in Go. Hope you found this all helpful!

And if you made it this far, Thank You. 🙌