Documentation
¶
Overview ¶
Package reflection provides utilities that extend Go's reflect package with practical helper functions for common reflection tasks.
The package offers:
- Safe pointer dereferencing and nil checking
- Struct field manipulation with support for anonymous embedded fields
- Field validation with custom validation functions
- Zero value detection for structs and fields
- Value conversion utilities
Most functions that work with structs support flattening of anonymous embedded fields, making them appear as top-level fields. This is useful for ORM-like operations, serialization, and validation.
Example usage:
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
user := User{Name: "Alice", Email: "[email protected]"}
// Get field names from struct tags
names := reflection.FlatStructFieldTagsOrNames(reflect.TypeOf(user), "json")
// names: ["name", "email"]
// Iterate over fields
for field, value := range reflection.FlatExportedStructFieldsIter(user) {
fmt.Printf("%s = %v\n", field.Name, value.Interface())
}
Index ¶
- Variables
- func DerefType(t reflect.Type) reflect.Type
- func DerefValue(val any) reflect.Value
- func DerefValueAndType(val any) (reflect.Value, reflect.Type)
- func EnumFlatExportedStructFields(val any, callback func(reflect.StructField, reflect.Value))
- func FlatExportedStructFieldValueNameMap(val any, nameTag string) map[string]StructFieldValueName
- func FlatExportedStructFieldsIter(s any) iter.Seq2[reflect.StructField, reflect.Value]
- func FlatStructFieldCount(t reflect.Type) int
- func FlatStructFieldNames(t reflect.Type) (names []string)
- func FlatStructFieldTags(t reflect.Type, tagKey string) (tagValues []string)
- func FlatStructFieldTagsOrNames(t reflect.Type, tagKey string) (tagsOrNames []string)
- func FlatStructFieldValues(v reflect.Value) (values []reflect.Value)
- func IsNil(v reflect.Value) bool
- func IsZero(v any) bool
- func ValueOf(val any) reflect.Value
- func ValuesToInterfaces(values ...reflect.Value) []any
- func ZeroValueExportedStructFieldNames(st any, namePrefix, nameTag string, namesToValidate ...string) (zeroNames []string)
- type FieldError
- type NamedStructField
- type StructFieldValue
- type StructFieldValueName
Constants ¶
This section is empty.
Variables ¶
var ( // TypeOfError is the reflect.Type of the error interface. // Useful for type comparisons when working with functions that return errors. // // Example: // // func returnsError(fn reflect.Type) bool { // return fn.NumOut() > 0 && fn.Out(0) == reflection.TypeOfError // } TypeOfError = reflect.TypeOf((*error)(nil)).Elem() // TypeOfEmptyInterface is the reflect.Type of the empty interface (any). // Useful for type comparisons when working with generic interfaces. TypeOfEmptyInterface = reflect.TypeOf((*interface{})(nil)).Elem() )
Functions ¶
func DerefType ¶
DerefType dereferences a type until a non-pointer type is found. It repeatedly follows pointer types until reaching a concrete type.
Example:
t := reflect.TypeOf((**int)(nil)) concrete := reflection.DerefType(t) fmt.Println(concrete.Kind()) // int
func DerefValue ¶
DerefValue dereferences val until a non-pointer type or nil pointer is found. It repeatedly follows pointers until reaching a concrete value or a nil pointer.
The argument val can be any value or a reflect.Value (handled by ValueOf).
Example:
x := 42 ptr := &x ptrPtr := &ptr v := reflection.DerefValue(ptrPtr) fmt.Println(v.Int()) // 42 var nilPtr *int v2 := reflection.DerefValue(nilPtr) fmt.Println(v2.IsValid()) // false (nil pointer)
func DerefValueAndType ¶
DerefValueAndType dereferences val until a non-pointer value is found and returns both the dereferenced value and its type.
This is a convenience function that combines DerefValue with getting the type.
Example:
x := 42 ptr := &x value, typ := reflection.DerefValueAndType(ptr) fmt.Println(value.Int(), typ.Kind()) // 42 int
func EnumFlatExportedStructFields ¶
func EnumFlatExportedStructFields(val any, callback func(reflect.StructField, reflect.Value))
EnumFlatExportedStructFields returns reflect.StructField and reflect.Value of flattened struct fields, meaning that the fields of anonoymous embedded fields are flattened to the top level of the struct. The argument val can be a struct, a pointer to a struct, or a reflect.Value.
func FlatExportedStructFieldValueNameMap ¶
func FlatExportedStructFieldValueNameMap(val any, nameTag string) map[string]StructFieldValueName
FlatExportedStructFieldValueNameMap returns a slice of StructFieldValueName of flattened struct fields, meaning that the fields of anonoymous embedded fields are flattened to the top level of the struct. The argument val can be a struct, a pointer to a struct, or a reflect.Value.
func FlatExportedStructFieldsIter ¶
FlatExportedStructFieldsIter returns an iterator over flattened exported struct fields. Anonymous embedded fields are flattened to the top level.
This is the most memory-efficient way to iterate over struct fields, as it doesn't allocate a slice. Requires Go 1.23+.
The argument s can be a struct, a pointer to a struct, or a reflect.Value.
Example:
type User struct {
Name string
Email string
}
user := User{Name: "Bob", Email: "[email protected]"}
for field, value := range reflection.FlatExportedStructFieldsIter(user) {
fmt.Printf("%s = %v\n", field.Name, value.Interface())
}
// Output:
// Name = Bob
// Email = [email protected]
func FlatStructFieldCount ¶
FlatStructFieldCount returns the number of flattened struct fields. Anonymous embedded fields are flattened, meaning their fields are counted as top-level fields of the struct.
Example:
type Base struct {
ID int
Name string
}
type Extended struct {
Base // Anonymous field - will be flattened
Email string
}
count := reflection.FlatStructFieldCount(reflect.TypeOf(Extended{}))
fmt.Println(count) // 3 (ID, Name, Email)
func FlatStructFieldNames ¶
FlatStructFieldNames returns the names of flattened struct fields. Anonymous embedded fields are flattened, meaning their field names appear at the top level alongside non-embedded fields.
Example:
type Address struct {
Street string
City string
}
type Person struct {
Name string
Address // Anonymous field
}
names := reflection.FlatStructFieldNames(reflect.TypeOf(Person{}))
fmt.Println(names) // [Name Street City]
func FlatStructFieldTags ¶
FlatStructFieldTags returns the tag values for a tagKey of flattened struct fields, meaning that the fields of anonoymous embedded fields are flattened to the top level of the struct. An empty string is returned for fields that don't have a matching tag.
func FlatStructFieldTagsOrNames ¶
FlatStructFieldTagsOrNames returns the tag values for tagKey or the names of the field if no tag with tagKey is defined at a struct field. Fields are flattened, meaning that the fields of anonoymous embedded fields are flattened to the top level of the struct.
func FlatStructFieldValues ¶
FlatStructFieldValues returns the values of flattened struct fields, meaning that the fields of anonoymous embedded fields are flattened to the top level of the struct.
func IsNil ¶
IsNil returns true if v is of a nillable type and is nil.
Unlike reflect.Value.IsNil(), this function is safe to call for any value and type. It will not panic for non-nillable types (like int, string, etc.).
Returns true for:
- Invalid/zero reflect.Value (result of reflect.ValueOf(nil))
- Nil pointers, interfaces, channels, functions, maps, or slices
Returns false for:
- Non-nillable types (int, string, struct, etc.)
- Non-nil values of nillable types
Example:
var ptr *int fmt.Println(reflection.IsNil(reflect.ValueOf(ptr))) // true var num int fmt.Println(reflection.IsNil(reflect.ValueOf(num))) // false (non-nillable type) var v reflect.Value fmt.Println(reflection.IsNil(v)) // true (invalid/zero value)
func IsZero ¶
IsZero returns true if the underlying value of v is the zero (default) value of its type, or if v itself is nil.
This uses reflect.DeepEqual to compare the value with its zero value, so it works correctly for structs, slices, maps, and other composite types.
Example:
fmt.Println(reflection.IsZero(0)) // true
fmt.Println(reflection.IsZero("")) // true
fmt.Println(reflection.IsZero(nil)) // true
fmt.Println(reflection.IsZero("hello")) // false
fmt.Println(reflection.IsZero([]int{})) // true (empty slice is zero)
func ValueOf ¶
ValueOf is an enhanced version of reflect.ValueOf that handles reflect.Value arguments. If val is already a reflect.Value, it returns val as-is without wrapping. Otherwise, it returns reflect.ValueOf(val).
This is useful when writing generic reflection code that might receive either regular values or already-reflected values.
Example:
v := reflect.ValueOf(42) result := reflection.ValueOf(v) // result is the same as v, not reflect.ValueOf(v) result2 := reflection.ValueOf(42) // result2 is reflect.ValueOf(42)
func ValuesToInterfaces ¶
ValuesToInterfaces converts a slice of reflect.Value to a slice of any (interface{}) by calling reflect.Value.Interface() for each value.
This is useful when you have reflect.Value objects and need to pass them to functions that accept any/interface{} parameters.
Example:
values := []reflect.Value{
reflect.ValueOf(42),
reflect.ValueOf("hello"),
reflect.ValueOf(true),
}
interfaces := reflection.ValuesToInterfaces(values...)
// interfaces: []any{42, "hello", true}
func ZeroValueExportedStructFieldNames ¶
func ZeroValueExportedStructFieldNames(st any, namePrefix, nameTag string, namesToValidate ...string) (zeroNames []string)
ZeroValueExportedStructFieldNames returns the names of exported struct fields that have zero (default) values.
Parameters:
- st: The struct value to examine (can be a struct, pointer to struct, or reflect.Value)
- namePrefix: A prefix to add to all returned field names
- nameTag: The struct tag key to use for field names (e.g., "json"). If empty or not found, uses Go field name
- namesToValidate: Optional list of specific field names to check. If empty, checks all fields
Behavior:
- Anonymous embedded structs are flattened
- Named sub-structs are checked recursively with their name as prefix (e.g., "Address.Street")
- Zero elements in arrays/slices are reported with index notation (e.g., "Items[1]")
- Struct tag values can include comma-separated options; only the part before the comma is used
- Fields with tag value "-" are ignored
Example:
type Form struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
Tags []string `json:"tags"`
}
form := Form{Name: "John", Tags: []string{"a", "", "c"}}
zeros := reflection.ZeroValueExportedStructFieldNames(form, "", "json")
// zeros: ["email", "age", "tags[1]"]
Types ¶
type FieldError ¶
type FieldError struct {
FieldName string // Name of the field that failed validation
FieldError error // The validation error for this field
}
FieldError represents a validation error for a specific struct field. It combines the field name with its validation error.
func ValidateStructFields ¶
func ValidateStructFields(validateFunc func(any) error, st any, namePrefix, nameTag string, namesToValidate ...string) (fieldErrors []FieldError)
ValidateStructFields validates all exported fields of a struct using a custom validation function.
The validation function is called three times for each field (if applicable):
- With the field value itself
- With the field value's address (if addressable)
- With the dereferenced value (if the field is a pointer and not nil)
This allows the validation function to work with values, pointers, and interface implementations.
Parameters:
- validateFunc: Function that validates a value and returns an error if invalid
- st: The struct to validate (can be a struct, pointer to struct, or reflect.Value)
- namePrefix: A prefix to add to all field names in errors
- nameTag: The struct tag key to use for field names (e.g., "json"). If empty, uses Go field name
- namesToValidate: Optional list of specific field names to validate. If empty, validates all fields
Behavior:
- Anonymous embedded structs are flattened
- Named sub-structs are validated recursively
- Array and slice elements are validated individually
- Returns a slice of FieldError for all fields that failed validation
Example:
func validateNotEmpty(val any) error {
if s, ok := val.(string); ok && strings.TrimSpace(s) == "" {
return errors.New("cannot be empty")
}
return nil
}
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
user := User{Name: "", Email: "[email protected]"}
errors := reflection.ValidateStructFields(validateNotEmpty, user, "", "json")
// errors: [FieldError{FieldName: "name", FieldError: errors.New("cannot be empty")}]
func (FieldError) Error ¶
func (f FieldError) Error() string
Error implements the error interface, formatting the error as "FieldName: error message".
type NamedStructField ¶
type NamedStructField struct {
Field reflect.StructField // Type information about the field
Name string // Custom name from struct tag or field name
}
NamedStructField combines field type information with a custom name. Unlike StructFieldValueName, this doesn't include the runtime value, making it suitable for type-level operations.
Example:
type Config struct {
Host string `json:"host"`
Port int `json:"port"`
}
fields := reflection.FlatExportedNamedStructFields(reflect.TypeOf(Config{}), "json")
for _, field := range fields {
fmt.Printf("%s: %s\n", field.Name, field.Field.Type)
}
// Output:
// host: string
// port: int
func FlatExportedNamedStructFields ¶
func FlatExportedNamedStructFields(t reflect.Type, nameTag string) []NamedStructField
FlatExportedNamedStructFields returns a slice of NamedStructField of flattened struct fields, meaning that the fields of anonoymous embedded fields are flattened to the top level of the struct. The argument t can be a struct, a pointer to a struct, or a reflect.Value.
type StructFieldValue ¶
type StructFieldValue struct {
Field reflect.StructField // Type information about the field
Value reflect.Value // Runtime value of the field
}
StructFieldValue combines a struct field's type information with its runtime value. This is useful for operations that need both the field metadata and its actual value.
Example:
type User struct {
Name string
Age int
}
user := User{Name: "Alice", Age: 30}
fields := reflection.FlatExportedStructFields(user)
for _, field := range fields {
fmt.Printf("%s (%s) = %v\n", field.Field.Name, field.Field.Type, field.Value.Interface())
}
// Output:
// Name (string) = Alice
// Age (int) = 30
func FlatExportedStructFields ¶
func FlatExportedStructFields(val any) []StructFieldValue
FlatExportedStructFields returns a slice of StructFieldValue of flattened struct fields, meaning that the fields of anonoymous embedded fields are flattened to the top level of the struct. The argument val can be a struct, a pointer to a struct, or a reflect.Value.
type StructFieldValueName ¶
type StructFieldValueName struct {
Field reflect.StructField // Type information about the field
Value reflect.Value // Runtime value of the field
Name string // Custom name from struct tag or field name
}
StructFieldValueName combines field type information, runtime value, and a custom name. The Name is typically derived from a struct tag (e.g., json, db, xml tags).
Example:
type Product struct {
ID int `db:"product_id"`
Name string `db:"product_name"`
Price float64 `db:"price"`
}
product := Product{ID: 1, Name: "Widget", Price: 9.99}
fields := reflection.FlatExportedStructFieldValueNames(product, "db")
for _, field := range fields {
fmt.Printf("%s = %v\n", field.Name, field.Value.Interface())
}
// Output:
// product_id = 1
// product_name = Widget
// price = 9.99
func FlatExportedStructFieldValueNames ¶
func FlatExportedStructFieldValueNames(val any, nameTag string) []StructFieldValueName
FlatExportedStructFieldValueNames returns a slice of StructFieldValueName of flattened struct fields, meaning that the fields of anonoymous embedded fields are flattened to the top level of the struct. The argument val can be a struct, a pointer to a struct, or a reflect.Value.