fieldval

package
v2.0.1 Latest Latest
Warning

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

Go to latest
Published: Jun 9, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package fieldval holds the internal value carrier (FieldValue) that flows through the validating pipeline. It depends only on goutil and the sibling internal/reflectx package, never on the validate root package, keeping the dependency direction one-way (root -> internal/fieldval).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type FieldValue

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

FieldValue is an internal value carrier that flows through the validating pipeline. It holds the raw value together with its lazily-built reflect.Value, so the same underlying value is not repeatedly re-reflected (reflect.ValueOf) at each pipeline stage (痛点 A / B in docs/validate-v2-design.md §4.3).

All computation is lazy and cached: rv/rt/real and the zero/empty tri-state flags are only computed on first access. Designed for single-goroutine use within one validation pass (a *Validation is not shared concurrently).

func New

func New(name string, src any) *FieldValue

New builds a FieldValue from a plain any value (map/form sources). The caller already holds the boxed value, so src is set eagerly; the reflect.Value is still built lazily on first need.

func NewRV

func NewRV(name string, rv reflect.Value) *FieldValue

NewRV builds a FieldValue from an already-available reflect.Value (struct source). It avoids an Interface()->ValueOf round-trip AND keeps the boxed value lazy: src is materialized (rv.Interface()) only on first Src() call, so RV-native consumers (IsEmpty/String/calcLen/required) never box.

IMPORTANT(R4 去装箱): 此函数必须保持 **可内联**(inlinable),否则它返回的 *FieldValue 会因从非内联函数返回而被 escape 分析判为逃逸到堆,使每个 struct 字段载体都堆分配(实测 CheckErrValid +N allocs)。为此把 invalid-RV 的冷分支 抽到独立的 newRVInvalid,让 NewRV 体足够小可内联;valid-RV(热路径)直接内联构造 &FieldValue{...} 进调用方栈帧, 与可内联的 New 同样栈分配。

func (*FieldValue) Indirect added in v2.0.1

func (f *FieldValue) Indirect() any

Indirect 返回单层指针解引用后的值(any),语义与 reflectx.IndirectValue(Src) 字节级一致: 未类型化 nil→nil;非指针→原样(Src,无新装箱);非空指针→解引用后的 Interface();空指针→nil。 复用缓存 RV(),避免二次 reflect.ValueOf。

func (*FieldValue) IsEmpty

func (f *FieldValue) IsEmpty() bool

IsEmpty reports whether the value is empty, giving results identical to the public IsEmpty(any) for the same input — without ever reading the boxed src.

Pure reflect.Value: RV() substitutes NilRVal (= reflect.ValueOf(NilObject{})) for an untyped-nil src, and reflects.IsEmpty(NilRVal) returns true via its DeepEqual default branch — matching public IsEmpty(nil)==true. A string src surfaces as Kind String, where reflects.IsEmpty does v.Len()==0, matching the `s == ""` fast path. So one call covers nil / string / everything.

func (*FieldValue) IsZero

func (f *FieldValue) IsZero() bool

IsZero reports whether the value is the zero value for its type, matching the reflect.Value.IsZero() semantics used by StructData.TryGet. Lazy + cached.

func (*FieldValue) RT

func (f *FieldValue) RT() reflect.Type

RT returns the reflect.Type of RV(), lazily built and cached. NewRV defers rt (rtInit=false) to keep itself inlinable, so RT() computes rv.Type() on first access here; New computes both rv+rt together in RV().

func (*FieldValue) RV

func (f *FieldValue) RV() reflect.Value

RV returns reflect.ValueOf(src), lazily built and cached.

If src is any(nil) the resulting reflect.Value is invalid; in that case it returns nilRVal — identical to the fix at callValidatorValue (validating.go), so fv.Call() won't panic on an Invalid kind (#125).

func (*FieldValue) RealV

func (f *FieldValue) RealV() reflect.Value

RealV returns the de-pointered reflect.Value.

Semantics are kept byte-for-byte identical to the previous inline handling in callValidatorValue (its only P1 consumer): a single non-nil pointer level is dereferenced; a nil pointer is left as-is (so typed-nil keeps its pointer type). Deeper pointer levels are intentionally NOT unwrapped here to preserve 100% behavior equivalence with the pre-refactor code.

func (*FieldValue) Src

func (f *FieldValue) Src() any

Src returns the original boxed value, materializing it lazily for NewRV carriers (only reached when a downstream consumer truly needs `any`). For New carriers src was set eagerly; for NewRV(invalid) src was pinned to nil at construction. So the remaining lazy case here is NewRV(valid): rv.Interface(), matching the previous eager `f.Src = rv.Interface()` exactly.

func (*FieldValue) SrcIfSet added in v2.0.1

func (f *FieldValue) SrcIfSet() (any, bool)

SrcIfSet returns the boxed src ONLY if it was already materialized (no new rv.Interface() boxing). Used by the skipCollect dedup to carry an already-boxed value to the next rule's carrier of the same field, so a validator that needs `any` (e.g. valueCompare on Age) boxes once across the field's rules instead of once per rule — without ever boxing fields whose validators stayed RV-native.

func (*FieldValue) String added in v2.0.1

func (f *FieldValue) String() (string, bool)

String 返回字段值的字符串形式,语义与 validating.go valToString 字节级一致, 但复用缓存的 RV()(命名字符串类型/可转换值都不 panic)。bool 报告是否取到可用字符串。

NOTE: 签名是 () (string, bool),不满足 fmt.Stringer,无冲突。

Jump to

Keyboard shortcuts

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