tinyreflect

package module
v0.2.2 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2025 License: MIT Imports: 2 Imported by: 1

README

Tiny Reflect

Project Badges

Minimal Go reflect package from reflectlite, WebAssembly and TinyGo destination.

It provides a global function API similar to Go's standard reflect package, optimized for performance in resource-constrained environments.

Why?

Go's WebAssembly potential is incredible, but traditional applications face a critical challenge: massive binary sizes that make web deployment impractical.

The Problem

Every Go project needs reflection capabilities for JSON operations, struct field access, and dynamic type handling - but importing the standard library reflect package creates significant binary bloat that hurts:

  • 🌐 Web app performance - Slow loading times and poor user experience
  • 📱 Edge deployment - Resource constraints on small devices
  • 🚀 Distribution efficiency - Large binaries for simple operations
The Solution

TinyReflect replaces the standard library reflect package with ultra-minimal, focused implementations that deliver:

  • 🏆 Dramatically smaller binaries - Significant size reduction for WebAssembly through maximum code reuse
  • Full TinyGo compatibility - No compilation issues or warnings
  • 🎯 Predictable performance - No hidden allocations or overhead
  • 🔧 Minimal API - Only essential operations for basic JSON-like data handling
  • 🌍 Multilingual error handling - Integrated with tinystring's error system
  • ♻️ Maximum code reuse - Leverages tinystring's type detection to minimize duplication
  • 📚 Go standard library API - Global functions like Go's reflect package
Supported Types (Minimalist Approach)

TinyReflect intentionally supports only a minimal set of types to keep binary size small:

✅ Supported Types:

  • Basic types: string, bool
  • All numeric types: int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64
  • All basic slices: []string, []bool, []byte, []int, []int8, []int16, []int32, []int64, []uint, []uint8, []uint16, []uint32, []uint64, []float32, []float64
  • Structs: Only with supported field types
  • Struct slices: []struct{...} where all fields are supported types
  • Maps: map[K]V where K and V are supported types only
  • Map slices: []map[K]V where K and V are supported types only
  • Pointers: Only to supported types above

❌ Unsupported Types:

  • any, chan, func
  • complex64, complex128
  • uintptr, unsafe.Pointer (used internally only)
  • Arrays (different from slices)
  • Nested complex types beyond supported scope

This focused approach ensures minimal code size while covering the most common JSON-like data operations including simple structs.

API Usage

TinyReflect provides global functions similar to Go's standard reflect package.

import "github.com/cdvelop/tinyreflect"

type User struct {
    Name string
    Age  int
}

// IMPORTANT: For struct name resolution, implement StructNamer interface
func (User) StructName() string {
    return "User"
}

u := User{"Alice", 42}

// Use global functions to get a reflected Value
v := tinyreflect.ValueOf(u)

// Get the reflected type
t := tinyreflect.TypeOf(u)

// Get the struct name (requires StructNamer interface)
name := t.Name() // Returns "User" if StructNamer is implemented, "struct" otherwise

// Iterate fields and get name and value
num, _ := t.NumField()
for i := 0; i < num; i++ {
    fieldName, _ := t.NameByIndex(i)
    field, _ := v.Field(i)
    value, _ := field.Interface()
    // fieldName: field name, value: field value
}

// Unique type identifier
id := t.StructID()

// Check if a field value is zero (useful for partial updates)
field, _ := v.Field(0)
if field.IsZero() {
    // Field has zero value
}
Minimal public API
Global Functions
  • TypeOf(i any) *Type — Returns the reflection Type that represents the dynamic type of i.
  • ValueOf(i any) Value — Returns a new Value initialized to the concrete value stored in the interface i.
  • Indirect(v Value) Value — Returns the value that a pointer v points to.
  • NewValue(typ *Type) Value — Returns a Value representing a pointer to a new zero value for typ.
  • MakeSlice(typ *Type, len, cap int) (Value, error) — Creates a new zero-initialized slice value.
Value Methods
  • Value.Type() *Type — Get the reflected type.
  • Value.Field(i int) (Value, error) — Get the i-th field of a struct value.
  • Value.NumField() (int, error) — Number of fields in a struct value.
  • Value.Kind() Kind — Get the kind of the value.
  • Value.CanAddr() bool — Reports whether the value's address can be obtained.
  • Value.IsZero() bool — Reports whether v is the zero value for its type.
  • Value.Elem() (Value, error) — Returns the value that the pointer points to.
  • Value.String() string — Returns the string representation of the value.
  • Value.Int() (int64, error) — Returns the value as int64.
  • Value.Uint() (uint64, error) — Returns the value as uint64.
  • Value.Float() (float64, error) — Returns the value as float64.
  • Value.Bool() (bool, error) — Returns the value as bool.
  • Value.InterfaceZeroAlloc(target *any) — Sets value to target pointer without boxing.
Type Methods
  • Type.Name() string — Get type name (requires StructNamer for structs).
  • Type.NumField() (int, error) — Number of fields in a struct.
  • Type.NameByIndex(i int) (string, error) — Name of the i-th field.
  • Type.Field(i int) (StructField, error) — Info about the i-th field.
  • Type.Kind() Kind — Base type (struct, int, string, etc).
  • Type.StructID() uint32 — Unique identifier for the struct type.

No functions related to methods, interfaces, or advanced reflection are exposed. The API is deliberately minimal and robust against misuse.

Important: Struct Name Resolution in TinyGo

TinyReflect requires struct types to implement the StructNamer interface to provide their name.

type StructNamer interface {
    StructName() string
}
Why This Limitation Exists

TinyGo removes type metadata (including struct names) from compiled binaries to reduce size. Unlike standard Go, runtime type name resolution is not available. To work around this:

  1. With StructNamer: Type.Name() returns the custom name
  2. Without StructNamer: Type.Name() returns "struct"
  3. Non-struct types: Type.Name() returns the kind name ("int", "string", etc.)
Example Implementation
import (
    "fmt"
    "github.com/cdvelop/tinyreflect"
)

type Customer struct {
    ID   int
    Name string
}

// Required for name resolution in TinyGo
func (Customer) StructName() string {
    return "Customer"
}

// Usage
c := Customer{ID: 1, Name: "Alice"}
typ := tinyreflect.TypeOf(c)
fmt.Println(typ.Name()) // Output: "Customer"

// Without StructNamer interface:
type Product struct {
    Title string
    Price float64
}
// No StructName() method defined

p := Product{Title: "Book", Price: 15.99}
typ2 := tinyreflect.TypeOf(p)
fmt.Println(typ2.Name()) // Output: "struct"

This approach ensures TinyGo compatibility while allowing applications that need struct names to opt-in via interface implementation.

Performance


Contributing

Documentation

Index

Constants

View Source
const (
	KindDirectIface Kind = 1 << 5
	KindMask        Kind = (1 << 5) - 1
)

Essential constants for type operations

Variables

This section is empty.

Functions

This section is empty.

Types

type ArrayType added in v0.0.32

type ArrayType struct {
	Type
	Elem  *Type   // array element type
	Slice *Type   // slice type
	Len   uintptr // array length
}

ArrayType represents an array type.

func (*ArrayType) Element added in v0.0.32

func (t *ArrayType) Element() *Type

Elem returns the element type of the array

func (*ArrayType) Length added in v0.0.32

func (t *ArrayType) Length() int

Length returns the length of the array

type EmptyInterface added in v0.0.13

type EmptyInterface struct {
	Type *Type
	Data unsafe.Pointer
}

EmptyInterface describes the layout of a "any" or a "any."

type Name added in v0.0.13

type Name struct {
	Bytes *byte
}

func (Name) DataChecked added in v0.0.13

func (n Name) DataChecked(off int, whySafe string) *byte

DataChecked does pointer arithmetic on n's Bytes, and that arithmetic is asserted to be safe for the reason in whySafe (which can appear in a backtrace, etc.)

func (Name) HasTag added in v0.0.13

func (n Name) HasTag() bool

HasTag returns true iff there is tag data following this name

func (Name) IsEmbedded added in v0.0.13

func (n Name) IsEmbedded() bool

IsEmbedded returns true iff n is embedded (an anonymous field).

func (Name) IsExported added in v0.0.13

func (n Name) IsExported() bool

IsExported reports whether the name is exported.

func (Name) Name added in v0.0.13

func (n Name) Name() string

Name returns the tag string for n, or empty if there is none.

func (Name) ReadVarint added in v0.0.13

func (n Name) ReadVarint(off int) (int, int)

ReadVarint parses a varint as encoded by encoding/binary. It returns the number of encoded bytes and the encoded value.

func (Name) String added in v0.0.13

func (n Name) String() string

String returns the name as a string.

func (Name) Tag added in v0.0.13

func (n Name) Tag() string

Tag returns the tag string for n, or empty if there is none.

type NameOff added in v0.0.13

type NameOff int32

NameOff is the Offset to a Name from moduledata.types. See resolveNameOff in runtime.

type PtrType added in v0.0.17

type PtrType struct {
	Type
	Elem *Type // pointer element type
}

PtrType represents a pointer type.

type SliceType added in v0.0.32

type SliceType struct {
	Type
	Elem *Type // slice element type
}

SliceType represents a slice type.

func (*SliceType) Element added in v0.0.32

func (t *SliceType) Element() *Type

Elem returns the element type of the slice

type StructField added in v0.0.13

type StructField struct {
	Name Name    // name is always non-empty
	Typ  *Type   // type of field
	Off  uintptr // offset within struct, in bytes
}

A StructField describes a single field in a struct.

func (*StructField) Embedded added in v0.0.13

func (f *StructField) Embedded() bool

func (StructField) Tag added in v0.0.13

func (f StructField) Tag() StructTag

Tag returns the field's tag as a StructTag.

type StructNamer added in v0.0.45

type StructNamer interface {
	StructName() string
}

StructNamer interface allows structs to provide their own name for reflection. This is required for TinyGo compatibility since runtime type name resolution is not available in TinyGo's limited reflection support.

Types implementing this interface will have their StructName() method called during TypeOf() to cache the struct name for later retrieval via Type.Name().

Example:

type User struct { Name string }
func (User) StructName() string { return "User" }

type StructTag added in v0.0.13

type StructTag string

StructTag is the tag string in a struct field (similar to reflect.StructTag)

func (StructTag) Get added in v0.0.13

func (tag StructTag) Get(key string) string

Get returns the value associated with key in the tag string. If there is no such key in the tag, Get returns the empty string.

type StructType added in v0.0.13

type StructType struct {
	Type
	PkgPath Name
	Fields  []StructField
}

type TFlag added in v0.0.13

type TFlag uint8

TFlag is used by a Type to signal what extra type information is available in the memory directly following the Type value.

type Type added in v0.0.13

type Type struct {
	Size        uintptr
	PtrBytes    uintptr // number of (prefix) bytes in the type that can contain pointers
	Hash        uint32  // Hash of type; avoids computation in Hash tables
	TFlag       TFlag   // extra type information flags
	Align_      uint8   // alignment of variable with this type
	FieldAlign_ uint8   // alignment of struct field with this type
	Kind_       Kind    // enumeration for C
	// function for comparing objects of this type
	Equal func(unsafe.Pointer, unsafe.Pointer) bool
	// GCData stores the GC type data for the garbage collector.
	// Normally, GCData points to a bitmask that describes the
	// ptr/nonptr Fields of the type. The bitmask will have at
	// least PtrBytes/ptrSize bits.
	// If the TFlagGCMaskOnDemand bit is set, GCData is instead a
	// **byte and the pointer to the bitmask is one dereference away.
	// The runtime will build the bitmask if needed.
	// (See runtime/type.go:getGCMask.)
	// Note: multiple types may have the same value of GCData,
	// including when TFlagGCMaskOnDemand is set. The types will, of course,
	// have the same pointer layout (but not necessarily the same size).
	GCData    *byte
	Str       NameOff // string form
	PtrToThis TypeOff // type for pointer to this type, may be zero
}

func TypeOf added in v0.0.13

func TypeOf(i any) *Type

TypeOf returns the reflection Type that represents the dynamic type of i. If i is a nil interface value, TypeOf returns nil.

func (*Type) ArrayType added in v0.0.32

func (t *Type) ArrayType() *ArrayType

ArrayType returns t cast to a *ArrayType, or nil if its tag does not match.

func (*Type) Elem added in v0.0.32

func (t *Type) Elem() *Type

Elem returns the element type for t if t is an array, channel, map, pointer, or slice, otherwise nil.

func (*Type) Field added in v0.0.13

func (t *Type) Field(i int) (StructField, error)

Field returns the i'th field of the struct type. It returns an error if the type is not a struct or the index is out of range.

func (*Type) IfaceIndir added in v0.0.13

func (t *Type) IfaceIndir() bool

IfaceIndir reports whether t is stored indirectly in an interface value.

func (*Type) Kind added in v0.0.13

func (t *Type) Kind() Kind

func (*Type) Name added in v0.0.16

func (t *Type) Name() string

Name returns the type's name within its package for a defined type. For other types, returns the kind name (e.g., "int", "string").

func (*Type) NameByIndex added in v0.0.19

func (t *Type) NameByIndex(i int) (string, error)

Name returns the name of a struct type's i'th field. It panics if the type's Kind is not Struct. It panics if i is out of range.

func (*Type) NumField added in v0.0.13

func (t *Type) NumField() (int, error)

NumField returns the number of fields in the struct type. It returns an error if the type is not a struct.

func (*Type) PtrType added in v0.0.32

func (t *Type) PtrType() *PtrType

PtrType returns t cast to a *PtrType, or nil if its tag does not match.

func (*Type) SliceType added in v0.0.32

func (t *Type) SliceType() *SliceType

SliceType returns t cast to a *SliceType, or nil if its tag does not match.

func (*Type) String added in v0.0.13

func (t *Type) String() string

func (*Type) StructID added in v0.0.16

func (t *Type) StructID() uint32

StructID returns a unique identifier for struct types based on runtime hash Returns 0 for non-struct types

func (*Type) StructType added in v0.0.13

func (t *Type) StructType() *StructType

StructType returns t cast to a *StructType, or nil if its tag does not match.

type TypeOff added in v0.0.13

type TypeOff int32

TypeOff is the Offset to a type from moduledata.types. See resolveTypeOff in runtime.

type Value added in v0.0.13

type Value struct {
	// contains filtered or unexported fields
}

func Indirect added in v0.0.32

func Indirect(v Value) Value

Indirect returns the value that v points to. If v is a nil pointer, Indirect returns a zero Value. If v is not a pointer, Indirect returns v.

func MakeSlice added in v0.0.32

func MakeSlice(typ *Type, len, cap int) (Value, error)

MakeSlice creates a new zero-initialized slice value for the specified slice type, length, and capacity.

func NewValue added in v0.2.0

func NewValue(typ *Type) Value

NewValue returns a Value representing a pointer to a new zero value for the specified type.

func ValueOf added in v0.0.13

func ValueOf(i any) Value

ValueOf returns a new Value initialized to the concrete value stored in the interface i. ValueOf(nil) returns the zero Value.

func (Value) Addr added in v0.0.32

func (v Value) Addr() (Value, error)

Addr returns a pointer value representing the address of v. It returns an error if CanAddr would return false.

func (Value) Bool added in v0.0.32

func (v Value) Bool() (bool, error)

Bool returns v's underlying value. It returns an error if v's Kind is not Bool.

func (Value) CanAddr added in v0.0.32

func (v Value) CanAddr() bool

CanAddr reports whether the value's address can be obtained with Value.Addr. Such values are called addressable. A value is addressable if it is an element of a slice, an element of an addressable array, a field of an addressable struct, or the result of dereferencing a pointer. If CanAddr returns false, calling Value.Addr will panic.

func (Value) Cap added in v0.0.32

func (v Value) Cap() (int, error)

Cap returns v's capacity. It returns an error if v's Kind is not Array, Chan, Slice or pointer to Array.

func (Value) Elem added in v0.0.17

func (v Value) Elem() (Value, error)

Elem returns the value that the pointer v points to. It panics if v's Kind is not Ptr. It returns the zero Value if v is a nil pointer.

func (Value) Field added in v0.0.13

func (v Value) Field(i int) (Value, error)

Field returns the i'th field of the struct v. Returns an error if v is not a struct or i is out of range.

func (Value) Float added in v0.0.32

func (v Value) Float() (float64, error)

Float returns v's underlying value, as a float64. It returns an error if v's Kind is not Float32 or Float64.

func (Value) Index added in v0.0.32

func (v Value) Index(i int) (Value, error)

Index returns v's i'th element. It returns an error if v's Kind is not Array, Slice, or String or i is out of range.

func (Value) Int added in v0.0.32

func (v Value) Int() (int64, error)

Int returns v's underlying value, as an int64. It returns an error if v's Kind is not Int, Int8, Int16, Int32, or Int64.

func (Value) Interface added in v0.0.19

func (v Value) Interface() (i any, err error)

Interface returns v's current value as an any. It is equivalent to:

var i any = (v's underlying value)

For a Value created from a nil interface value, Interface returns nil.

func (Value) InterfaceZeroAlloc added in v0.0.45

func (v Value) InterfaceZeroAlloc(target *any)

InterfaceZeroAlloc sets v's current value to the target pointer without any boxing. This method eliminates any boxing allocations for primitive types by directly manipulating the any structure to avoid the boxing that occurs when returning any.

For primitive types (int, string, bool, float64, etc.), it assigns the actual value directly to the any structure without creating boxing overhead.

For complex types (slices, maps, structs, etc.), it falls back to the standard Interface() method which does create boxing, but this is unavoidable for complex types.

This optimization is particularly beneficial for high-performance code that needs to extract primitive values from reflection without the boxing overhead.

func (Value) IsNil added in v0.0.32

func (v Value) IsNil() (bool, error)

IsNil reports whether its argument v is nil. It returns an error if v's Kind is not Chan, Func, Interface, Map, Pointer, or Slice.

func (Value) IsZero added in v0.0.48

func (v Value) IsZero() bool

IsZero reports whether v is the zero value for its type. It mirrors reflect.Value.IsZero() behavior for supported types.

func (Value) Kind added in v0.0.32

func (v Value) Kind() Kind

Kind returns the specific kind of this value.

func (Value) Len added in v0.0.32

func (v Value) Len() (int, error)

Len returns v's length. It returns an error if v's Kind is not Array, Chan, Map, Slice, or String.

func (Value) NumField added in v0.0.13

func (v Value) NumField() (int, error)

func (Value) Set added in v0.0.32

func (v Value) Set(x Value) error

Set assigns x to the value v. It returns an error if CanSet would return false.

func (Value) SetBool added in v0.0.32

func (v Value) SetBool(x bool) error

SetBool sets the bool value to the field represented by Value.

func (Value) SetBytes added in v0.0.32

func (v Value) SetBytes(x []byte) error

SetBytes sets the byte slice value to the field represented by Value.

func (Value) SetFloat added in v0.0.32

func (v Value) SetFloat(x float64) error

SetFloat sets the float value to the field represented by Value.

func (Value) SetInt added in v0.0.32

func (v Value) SetInt(x int64) error

SetInt sets the int value to the field represented by Value.

func (Value) SetString added in v0.0.17

func (v Value) SetString(x string) error

SetString sets the string value to the field represented by Value. It uses unsafe to write the value to the memory location of the field.

func (Value) SetUint added in v0.0.32

func (v Value) SetUint(x uint64) error

SetUint sets the uint value to the field represented by Value.

func (Value) String added in v0.0.32

func (v Value) String() string

String returns the string v's underlying value, as a string. String is a special case because of Go's String method convention. Unlike the other getters, it does not panic if v's Kind is not String. Instead, it returns a string of the form "<Translate value>" where Translate is v's type.

func (Value) Type added in v0.0.19

func (v Value) Type() *Type

Type returns v's type.

func (Value) Uint added in v0.0.32

func (v Value) Uint() (uint64, error)

Uint returns v's underlying value, as a uint64. It returns an error if v's Kind is not Uint, Uint8, Uint16, Uint32, or Uint64.

Jump to

Keyboard shortcuts

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