reflection

package
v9.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 3, 2026 License: AGPL-3.0 Imports: 4 Imported by: 0

Documentation

Overview

Package reflection provides utilities for struct field inspection, tag extraction, and dynamic method introspection.

The primitives in this package exist because the same three or four steps get open-coded at every call site that touches reflect, and they get open-coded slightly differently each time: whether a nil pointer is an error or an absence, whether a tag's options belong to its name, whether an embedded field is promoted or named. Each is a decision with a defensible answer, and the point of gathering them here is that the answer is given once.

StructValue and StructType are the two entry points. They differ in what they can accept, and the difference is not incidental: a nil *T has no fields to read but does have fields to describe, so a caller inspecting values needs StructValue's present report while a caller describing a shape can take StructType's happy answer.

FieldName resolves the name a field is encoded under for a given tag key, following encoding/json's convention — name up to the first comma, "-" to omit, "-," to name a field "-". It reports whether the name was explicit, which is what a caller needs to decide whether an embedded field is flattened or treated as a named object.

DerefOrZero descends through an absent pointer by substituting a zero value. That is right for a walk comparing two values field by field and wrong for a walk matching on values, where every zero field of the substitute would match a zero needle; the doc comment says so, and callers should heed it.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotAStruct indicates a value that is neither a struct nor a pointer to
	// one, and so has no fields to inspect.
	ErrNotAStruct = errors.New("reflection: value is not a struct or a pointer to one")

	// ErrNoValue indicates an argument that carried no value at all: an untyped
	// nil, an invalid reflect.Value, or a nil pointer. It is distinct from
	// ErrNotAStruct because a nil *T still names a type, and callers that only
	// need the type — GetFieldTypes, for one — can proceed where callers that
	// need the fields' values cannot.
	ErrNoValue = errors.New("reflection: no value to inspect")

	// ErrNoMatchingField indicates a search that completed without finding the
	// field it was looking for.
	ErrNoMatchingField = errors.New("reflection: no matching field found in struct")
)

Functions

func DerefOrZero

func DerefOrZero(v reflect.Value) reflect.Value

DerefOrZero dereferences a pointer, yielding the zero value of its element type when it is nil. A non-pointer is returned unchanged.

It is for walks that must keep descending through an absent pointer rather than stopping at it — comparing two structs field by field when one side's embedded pointer is nil, say. Note that this is the right behavior only when the zero value is a meaningful stand-in for absence. A search matching on field values should skip a nil pointer instead, because every zero field of the substitute would match a zero needle.

func FieldName

func FieldName(field *reflect.StructField, tagKey string) (name string, explicit, skip bool)

FieldName resolves the name a struct field is encoded under for tagKey, reporting whether the name was given explicitly by the tag and whether the field is skipped entirely.

The convention is encoding/json's, which most tag-driven encoders share: the value up to the first comma is the name, anything after it is options, an absent or empty tag means the field's own name, and "-" means omit. The one piece of trivia worth having written down once is that json:"-" omits a field while json:"-," names it "-"; both are honored here so that a caller describing a struct and an encoder writing it never disagree about which fields exist.

The explicit return distinguishes a name the tag gave from one defaulted to the field's own name. Callers flattening embedded fields need it: an anonymous field with no name of its own is promoted, while one carrying an explicit name is a named object in the encoded form.

field is taken by pointer only because reflect.StructField is large enough that passing it by value trips the hugeParam check.

func GetFieldTypes

func GetFieldTypes(strukt any) (map[string]any, error)

GetFieldTypes returns a map of field names to their types. For nested structs, the value is a map[string]any containing the nested struct's fields.

The argument may be a struct, a pointer to one, a nil pointer to one, or a reflect.Type naming one: this describes a shape and so needs only the type.

Fields are keyed by their Go names, not by any tag — this reports what the type declares, not what an encoder would write. Embedded structs are therefore recorded as a nested entry under the embedded type's name rather than flattened; use GetTagNameByValue or FieldName for the encoded view.

func GetMethodName

func GetMethodName(method any) string

GetMethodName is meant to fetch the name of a given method passed in as an argument.

func GetTagNameByValue

func GetTagNameByValue(strukt, fieldValue any, tagKey string) (string, error)

GetTagNameByValue searches struct strukt (or *strukt) for a field whose value equals fieldValue and returns the name that field is encoded under for tagKey (e.g. "json").

The name is resolved by FieldName, so it is the tag's name with any options stripped — a field tagged `json:"name,omitempty"` reports "name" — and a field with no tag for tagKey reports its own name rather than the empty string. A field the tag omits entirely is never matched.

Notes and limitations:

  • Values are compared with reflect.DeepEqual.
  • Unexported fields are skipped, since their values cannot be read.
  • The first match in struct field order wins, so a struct with two fields holding equal values reports the earlier one. Passing a value that several fields could hold — a zero string, say — is therefore ambiguous by construction.
  • Embedded fields are flattened exactly as encoding/json promotes them: an anonymous field with no explicit tag name is searched through, while one carrying a name is a named object and is compared whole. An embedded field of an unexported type that carries a name is therefore invisible: an embedded field's name is its type's name, so it cannot be read to be compared, and the tag has opted it out of being searched through.
  • A nil embedded pointer is skipped rather than stood in for by a zero struct. Substituting one would make every zero field of the substitute match a zero fieldValue, reporting a field that holds nothing as the field that holds what was asked for.
  • This requires the originating struct instance; a bare field value alone is insufficient in Go.

func StructType

func StructType(v any) (reflect.Type, error)

StructType resolves an argument to the struct type underneath it. The argument may be a struct, a pointer to one, a nil pointer to one, or a reflect.Type naming one.

This is the type-only counterpart to StructValue, and it exists because the two answer different questions: a nil *T has no fields to read but does have fields to describe.

func StructValue

func StructValue(v any) (value reflect.Value, present bool, err error)

StructValue resolves an argument to the struct value underneath it, reporting whether there was one at all.

The three ways a Go argument can be absent — an untyped nil interface, an invalid reflect.Value, and a typed nil pointer — all collapse to present=false with a nil error. That is the distinction most callers of reflect get wrong: a nil *T is not an error, it is a value that exists in the type system and not at runtime, and whether that is fatal depends on the caller. Callers that need a value return ErrNoValue on !present; callers that only need a type should use StructType, which accepts a nil pointer happily.

A non-nil argument that is not a struct is ErrNotAStruct, wrapped with the kind that was passed instead.

Types

This section is empty.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL