reflect2
A Go reflection API that avoids the runtime cost of reflect.Value by working with unsafe pointers directly.
Repository Health
Technical Analysis
reflect2 re-implements the parts of Go’s standard reflect package that matter for high-performance serialization code, but skips the boxing and dispatch overhead that comes with reflect.Value. Instead it exposes a Type interface backed either by raw unsafe.Pointer operations (the default, fastest path) or by a pure-reflect-based safe fallback, so callers can choose speed versus portability at configuration time.
It was extracted from json-iterator/go to be reused anywhere Go code needs to get or set values, walk struct fields, or resolve types by name at runtime without paying reflect’s usual performance tax. The package is intentionally low-level and is meant to be a building block inside other libraries rather than something application code reaches for directly.
What You Get
- A
Typeinterface with unsafe get/set,New/UnsafeNew, andPackEFace/Indirectoperations that avoid allocating and unwrappingreflect.Value reflect2.TypeByNameandTypeByPackageName, aClass.forName-style runtime type lookup built on the Go runtime’s type-link tables- Specialized interfaces for structs, slices, arrays, maps, and pointers (
StructType,SliceType,MapType,PtrType) with typed field/index/key accessors - A
Config/Froze()pattern (ConfigUnsafe/ConfigSafe) that lets callers switch the whole API between the unsafe fast path and a pure-reflect safe path - Helpers like
reflect2.IsNil,PtrOf,RTypeOf, andNoEscapefor working directly with Go’s internaleface/ifacerepresentations
Common Use Cases
- Building a JSON, MessagePack, or other binary encoder/decoder that needs to set struct fields and slice elements without reflect.Value allocation overhead
- Implementing a serialization library that must support a safe, pure-Go fallback for environments where unsafe operations are restricted
- Writing an ORM or struct-mapping library that walks struct tags and fields at high throughput
- Looking up a type by its fully-qualified name at runtime, similar to Java’s
Class.forName, for plugin or registry systems - Any low-level Go library that repeatedly reflects over the same types and wants to cache type metadata behind a stable interface
Under The Hood
Architecture
reflect2 is organized as a thin dispatch layer (reflect2.go) that wraps a reflect.Type into one of several concrete implementations chosen by frozenConfig.wrapType based on reflect.Kind — unsafeStructType, unsafeSliceType, unsafeMapType, unsafePtrType, unsafeEFaceType/unsafeIFaceType, or a generic unsafeType, each with a safe* counterpart used when Config.UseSafeImplementation is set. Every wrapped type is cached in a sync.Map keyed by the runtime type pointer inside frozenConfig, so repeated TypeOf/Type2 calls for the same type are effectively free after the first. type_map.go adds a separate, lazily-initialized global registry (types, packages) built once via typelinks2, the runtime’s linker-generated type-link table, giving TypeByName/TypeByPackageName their name-based lookup. What breaks if the core abstraction changes: any code relying on the internal eface/iface struct layout (unpackEFace, packEFace) is tied directly to Go’s runtime memory layout for interface values, so the library must be updated in lockstep with changes to that layout.
Tech Stack
The project is pure Go (module github.com/modern-go/reflect2, go.mod declares go 1.12) with zero runtime dependencies beyond the standard library (reflect, runtime, sync, unsafe). Build-tag-gated files (go_above_118.go, go_below_118.go, go_above_19.go) adapt to internal reflect/runtime layout changes across Go versions, and a !gccgo build constraint on type_map.go acknowledges that the typelinks2/linkname trick is specific to the standard gc toolchain. A legacy Gopkg.toml/Gopkg.lock pair shows the project predates Go modules and was originally vendored via dep; CI is configured through a minimal .travis.yml.
Code Quality
No test files (_test.go) exist in this repository — behavioral verification for reflect2 is left entirely to its consumers, most notably json-iterator/go’s own extensive test suite exercising this package indirectly. Code style is consistent and narrowly scoped: each Go kind (struct, slice, map, array, pointer, interface) gets its own small file pair (unsafe_*.go / safe_*.go), naming mirrors the standard reflect package’s vocabulary closely, and panics via assertType are used for programmer-error type mismatches rather than returned errors, which is a deliberate low-level-library tradeoff rather than an oversight.
What Makes It Unique
Rather than wrapping reflect.Value and eating its allocation and dynamic-dispatch cost on every get/set, reflect2 caches a wrapped-type object once and then reuses raw unsafe.Pointer operations (typedmemmove, unsafe_New) for subsequent field access — trading the standard library’s safety checks for throughput in a narrow, well-documented way, while still shipping a same-interface safe implementation so callers aren’t locked into unsafe code paths.
Used by 3 apps in this directory
Grafana
Monitoring · Analytics
The open-source observability platform that unifies metrics, logs, and traces from any data source into dynamic, queryable dashboards.
Jitsu
Data Engineering
Open-source, fully-scriptable data ingestion engine that streams events from web, apps, and APIs to any data warehouse in real time.
PeerDB
Data Engineering · Databases
Postgres-native ETL that streams change data capture in real time to Snowflake, BigQuery, ClickHouse, S3, and Kafka — up to 10x faster than general-purpose pipelines, managed through a familiar Postgres SQL interface.