Go by Example: Tagged Struct Fields
Struct tags are metadata attached to fields, used heavily by libraries like `encoding/json` and `validator`. This example explores how to define custom tags and parse them using reflection.
Code
package main
import (
"fmt"
"reflect"
"strings"
)
// User struct with custom validation tags
type User struct {
Username string `validate:"required,min=3"`
Email string `validate:"required,email"`
Age int `validate:"min=18"`
}
// Simple validator function that parses our custom tags
func validate(u interface{}) []string {
var errors []string
v := reflect.ValueOf(u)
t := reflect.TypeOf(u)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
tag := field.Tag.Get("validate")
if tag == "" {
continue
}
rules := strings.Split(tag, ",")
for _, rule := range rules {
if rule == "required" {
if value.IsZero() {
errors = append(errors, fmt.Sprintf("%s is required", field.Name))
}
}
// (Simplified: real validation logic would go here for min, email, etc.)
}
}
return errors
}
func main() {
// 1. Valid User
u1 := User{Username: "gopher", Email: "[email protected]", Age: 25}
fmt.Println("u1 errors:", validate(u1))
// 2. Invalid User (Empty fields)
u2 := User{Age: 10}
fmt.Println("u2 errors:", validate(u2))
// 3. Inspecting Tags
field, _ := reflect.TypeOf(User{}).FieldByName("Email")
fmt.Println("Email tag:", field.Tag)
}
Explanation
Tagged struct fields in Go represent a sophisticated metaprogramming feature that allows developers to attach arbitrary string metadata to struct fields, which can then be introspected at runtime using the reflect package to alter program behavior dynamically. While the Go compiler itself ignores the content of these tags, they form the backbone of the language's ecosystem for serialization (like JSON or XML), database object mapping (ORM), and input validation, effectively enabling declarative programming patterns within a statically typed language. By defining tags as key-value pairs enclosed in backticks, developers can specify constraints, formatting rules, or mapping directives that are decoupled from the business logic but tightly integrated with the data definition.
The mechanism for accessing these tags involves utilizing Go's reflection API, specifically reflect.Type, to iterate over the fields of a struct and retrieve the tag string associated with a specific key using the Get or Lookup methods. This capability allows library authors to build powerful, generic tools that can operate on any user-defined struct without knowing its layout at compile time, such as a validator that automatically checks if a field meets "required" or "max length" criteria based solely on its tag. This pattern significantly reduces boilerplate code and enhances maintainability by centralizing configuration directly on the data model.
- Metadata: Key-value pairs in backticks (e.g.,
key:"value"). - Reflection: Use
reflect.TypeOf()to access tags. - Ecosystem: Used by JSON, XML, ORMs, and validators.
- Decoupling: Separates configuration from logic.

