cast

package module
v2.1.1 Latest Latest
Warning

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

Go to latest
Published: May 4, 2026 License: MIT Imports: 16 Imported by: 0

README

cast

Now with Generics!

Release GoDoc Go Report Card Github issues Github pull requests MIT

CHANGELOG - This project adheres to Semantic Versioning. You should expect API stability in Minor and Patch version releases

Mature Code has proven satisfactory and is in wide production use, cleanup of the underlying code may cause some minor changes. Backwards-compatibility is guaranteed.

This project is inspired by spf13/cast. More about cast.

Table of Contents

What is Cast?

cast is a library to easily convert between different data types in a straightforward and predictable way. It provides a generic function to easily convert both simple types (number to a string, interface to a bool, etc.) and complex types (slice to map, any to func() any, any to chan any, etc.). Cast does this intelligently when an obvious conversion is possible and logically when a conversion requires a predictable measurable process.

A concrete example: casting "6.789" to int yields 6, not 7. Cast converts to float64 first, then calls math.Floor() — because there is no integer that is almost 7, but there is a 6 that can be contained within the original value.

The primary use-case is consuming untyped or loosely typed data from external sources (YAML, TOML, JSON, API responses) without repetitive type-assertion boilerplate.

Usage

// To returns the cast value; errors are silently dropped (zero value returned on failure).
func To[T Types](v any, o ...Op) T

// ToE returns the cast value and any error.
func ToE[T Types](v any, o ...Op) (T, error)

// ToStruct and ToStructE hydrate any struct type T from a map or struct source.
// T is constrained to any (not Types), so arbitrary struct types are accepted.
func ToStruct[T any](v any, o ...Op) T
func ToStructE[T any](v any, o ...Op) (T, error)

T in To/ToE is constrained to cast.Types, which covers all scalar types (bool, int*, uint*, float*, complex*, string), []T slices, map[K]V maps, chan T channels, cast.Func[T] closures, and stdlib named types (time.Time, time.Duration, net.IP, *url.URL, *regexp.Regexp, *big.Int, *big.Float). Named types with a matching underlying kind also satisfy Types (e.g. type Celsius float32).

On error, To returns the zero value for T; ToE returns the zero value plus the error. Use ToE when you need to distinguish a successful zero-value cast from a failed one.

Supported Conversions
cast.To[T](v, opts...)   // T; drops errors (zero value on failure)
cast.ToE[T](v, opts...)  // (T, error)

T covers all scalars, named underlying types (type Celsius float32), []T slices, map[K]V maps, chan T channels, and cast.Func[T] closures. Struct hydration uses cast.ToStruct[T] / cast.ToStructE[T].

[!IMPORTANT] Things that may surprise you

  • Float → int truncates, it doesn't round1.9 → 1, -1.9 → -1
  • stringbool is strict — only "1", "t", "true" and their case variants; "yes", "on", etc. are not accepted
  • Negative → uint* errors — use Op{ABS, true} to use the absolute value instead

Legend: always succeeds · ~ succeeds for valid input · always errors

chan T and Func[T] succeed whenever source → T succeeds — the cast result is wrapped in a buffered channel or closure. Nesting is supported (chan []int, Func[chan int], etc.). any targets always succeed.

Source bool int* uint* float* · complex* string []T map[K]V struct
bool
int* (signed)
uint* (unsigned)
float* · complex* ~²³ ~¹² ✓³
string ~⁴ ~⁵ ~⁵ ~⁵
[]byte · []rune ~ ~ ~ ~ ✓⁶ ~ᵐ ~ᵐ
[]T · [N]T ~⁷ ✓⁸
map[K]V ~⁷ ~ᵇ
struct ~⁷ ✓⁹ ~ᵇ
nil
error · Stringer ~ᵃ ~ᵃ ~ᵃ ~ᵃ ✓ᵃ
any / interface
Named types† ~ ~ ~ ~

int* = int · int8 · int16 · int32 · int64  ·  uint* = uint · uint8 · uint16 · uint32 · uint64 · uintptr
float* = float32 · float64  ·  complex* = complex64 · complex128
† Named types: time.Time · time.Duration · net.IP · *url.URL · *regexp.Regexp · *big.Int · *big.Float — see Named-type targets; all implement fmt.Stringer, so casting from them follows the string-parse path.


Number conversions

¹ Negative signed or float → uint* errors by default. Pass Op{ABS, true} to use the absolute value instead.
² float/complex → int*: truncates toward zero — 1.9 → 1, -1.9 → -1. Does not round.
³ complex → int*/uint*/float*: the imaginary part is discarded; only the real component is used. complex64 sources carry float32 precision, so complex64 → float64 has the same precision loss as float32 → float64.

String conversions

stringbool: only "1"/"0"/"t"/"f"/"true"/"false" and their case variants are accepted.
string → numeric: parsed as float64 via strconv.ParseFloat; non-numeric strings error. Float strings truncate when targeting int*.
[]byte and []runestring: uses string(b) / string(r) directly — not element-wise and not JSON-encoded. Scalar targets work the same way, via this string representation.
⁷ Complex types, maps, and structs → string: stringified via fmt.Sprintf("%v", v).

Container conversions

[]T/[N]Tmap[K]V: element indices (0, 1, 2 …) become map keys, cast to key type K.
structmap[K]V: exported field names become keys; embedded structs are inlined; nested structs recurse into nested maps when the value type is any or map.
error/Stringer → any target: calls .Error() or .String(), then parses the result the same way a plain string source would. Succeeds whenever the string value would succeed.
map/structstruct: source keys/fields matched case-sensitively to target exported fields. Unmatched fields retain their zero value; STRICT promotes mismatches to errors. Anonymous fields are promoted on both sides. Use ToStruct[T] / ToStructE[T] for arbitrary struct types.
ˡ string[]T: []byte and []rune use direct Go string conversion. All other element types attempt scalar wrap (e.g. "42"[]int{42}); if that fails and the string looks like a JSON collection, it is decoded automatically. Pass Op{DECODE, "json"} to force JSON decoding first.
[]byte ([]uint8) and []rune ([]int32) are treated as slices for container targets: elements are cast individually when converting to []T or map[K]V.
ˢ Non-collection sources wrap as single-element slices for []T targets: 42 → []int{42}. Structs iterate exported fields; maps iterate values; nil produces [zero_T].

Interface targets (error, fmt.Stringer) — the source must already implement the interface; no parsing occurs. Sources that don't implement the interface always error.

Named-type targets

time.Time, time.Duration, net.IP, *url.URL, *regexp.Regexp, *big.Int, and *big.Float are first-class cast targets via To[T] / ToE[T]. Any source that stringifies to a valid representation is accepted. All support the DEFAULT op and struct field hydration.

Source time.Time time.Duration net.IP *url.URL *regexp.Regexp *big.Int *big.Float
same named type ✓ᵃ ✓ᵃ
*time.Time ~ᵇ
time.Time (as source)
url.URL (value, not ptr)
big.Int · big.Float (values) ✓ᵗ ✓ᵗ ~ ✓ᶜ
*big.Float*big.Int ✓ᵗ ✓ᵗ ~ ~ᶜ
*big.Int*big.Float ✓ᵗ ✓ᵗ ~
int* · uint* ✓ᵈ ~ᵖ
float* ✓ᵉ ~ᶜ
string ~ᶠ ~ᵍ ~ ~ ~ ~
[]byte ~ᶠ ~ ~ⁱ ~ ~
uint32 ✓ᵈ ✓ⁱ
nil
any / interface

time.Time targets

int* / uint*time.Time: treated as Unix seconds — time.Unix(n, 0).UTC(). uint32 follows the same rule.
float*time.Time: treated as Unix seconds; fractional seconds preserved via nanosecond conversion.
string / []bytetime.Time: 19 formats tried in order — RFC3339Nano, RFC3339, DateTime, RFC1123Z, RFC1123, RFC822Z, RFC822, DateOnly, then Layout/ANSIC/UnixDate/RubyDate/RFC850/Kitchen/Stamp/StampMilli/StampMicro/StampNano/TimeOnly.
big.Int / big.Float (and pointer variants) → time.Time: integer part as Unix seconds. → time.Duration: as nanoseconds (*big.Int must fit in int64; *big.Float truncated toward zero). → net.IP: zero-padded to 16 bytes big-endian as IPv6; negative or > 128-bit values error.

net.IP targets

uint32 / int32net.IP: packed big-endian IPv4 — net.IPv4(b3, b2, b1, b0). int32 must be non-negative. []byte of exactly 4 or 16 bytes: direct copy into net.IP; other lengths parsed as string.
ᵖ Among int*, only int32net.IP is supported; other sizes error. For uint*, only uint32 has a dedicated IPv4 path; other sizes fall through to string-parse.

*big.Int / *big.Float targets

ᵃ Same-type cast returns an independent copy — new(big.Int).Set(src) / new(big.Float).Copy(src).
*big.Float / big.Float / float**big.Int: truncated toward zero. NaN/±Inf inputs error.
ʰ string*big.Int: base auto-detected — 0x hex, 0o octal, 0b binary, decimal otherwise.

General

*time.Timetime.Time: dereferenced when non-nil; nil pointer errors.
stringtime.Duration: time.ParseDuration — accepts "ns", "µs"/"us", "ms", "s", "m", "h" and combinations (e.g. "1h30m45s").
ʲ Via toString fallback — any source that successfully stringifies is passed to the string-parse path. *url.URL almost always succeeds (url.Parse is very permissive). *regexp.Regexp succeeds whenever the stringified form is a valid regexp pattern.

Options

Both functions accept optional Op values that control conversion behavior. Each Op carries a Flag constant and a value. Multiple options may be passed:

result := cast.To[float32](val, cast.Op{cast.DEFAULT, float32(3.14)})

items := []any{1, "two", true, 1}
result, err := cast.ToE[[]string](items, cast.Op{cast.UNIQUE_VALUES, true})
// result = []string{"1", "two", "true"}  (duplicate 1 removed)

Available flags

Flag Applies to Effect
DEFAULT all targets Return this value on error instead of the zero value
ABS uint* targets Use absolute value for negative signed inputs instead of erroring
DECODE bool, int*, uint*, float*, complex*, []T (string/error/Stringer source) Decode the source string as the specified format before converting; only "json" is supported. For scalar targets it fires as a fallback after normal parsing fails (e.g. "\"1\""1). For []T it forces JSON decoding first, bypassing []byte/[]rune special-casing. Note: map[K]V already auto-decodes JSON-like strings without this flag.
LENGTH []T, chan T Pre-allocate slice capacity or set channel buffer size (≥ 1 for chan)
UNIQUE_VALUES []T Deduplicate after conversion, preserving first-seen order
JSON string JSON-encode the resulting string (adds quotes and escaping)
PRIVATE map from struct; struct from map/struct Include unexported fields when reading a struct source or hydrating a struct target
STRICT map from struct; struct from map/struct Error instead of silently skipping unconvertible fields or unmatched keys
DUPLICATE_KEY_ERROR map from map Error when two source keys cast to the same target key
Examples
Scalars
Bool

Only specific string values are accepted; "yes", "on", and similar are not supported.

cast.To[bool](1)       // true
cast.To[bool](0)       // false
cast.To[bool]("true")  // true  — also "TRUE", "True", "t", "T", "1"
cast.To[bool]("false") // false — also "FALSE", "False", "f", "F", "0"
cast.To[bool]("yes")   // false — "yes"/"no"/"on"/"off" are not accepted
cast.To[bool](nil)     // false
Int and Float

Float-to-int truncates toward zero via math.Floor; it does not round.

cast.To[int](8.31)   // 8   — Floor, not round
cast.To[int]("8.51") // 8   — string → float64 → Floor
cast.To[int](true)   // 1
cast.To[int](nil)    // 0

// Negative → unsigned: errors by default; ABS takes the absolute value instead
cast.To[uint](-5)                               // 0 (error silently dropped)
cast.To[uint](-5, cast.Op{cast.ABS, true})      // 5
String
cast.To[string](8)                  // "8"
cast.To[string](8.31)               // "8.31"
cast.To[string]([]byte("hi"))       // "hi" — string(b) directly, not element-wise
cast.To[string](true)               // "true"
cast.To[string](nil)                // ""

// JSON-encode the result (adds quotes and escaping)
jsonStr, _ := cast.ToE[string](`hello "world"`, cast.Op{cast.JSON, true})
// jsonStr = `"hello \"world\""`
Slices

Slices, arrays, and maps convert element-wise. Scalar sources, nil, structs, and error/Stringer values are wrapped as single-element slices. nil always produces [zero_T].

cast.To[[]int]([]string{"1", "2", "3"})           // []int{1, 2, 3}
cast.To[[]string]([]int{1, 2, 3})                 // []string{"1", "2", "3"}
cast.To[[]bool]([]int{1, 0, 1})                   // []bool{true, false, true}

// Scalar sources wrap as single-element slices
cast.To[[]int](42)        // []int{42}
cast.To[[]string](3.14)   // []string{"3.14"}
cast.To[[]int](nil)       // []int{0}  — nil produces [zero_T]

// Map source: map values become slice elements; iteration order is undefined
cast.To[[]string](map[string]int{"a": 1, "b": 2}) // []string{"1", "2"} (order varies)

// Struct source: exported field values become slice elements
type Point struct{ X, Y int }
cast.To[[]int](Point{X: 3, Y: 4})    // []int{3, 4}
cast.To[[]string](Point{X: 3, Y: 4}) // []string{"3", "4"}

// DECODE=json for scalars: fallback after normal parse fails
// `"1"` is a JSON-encoded string containing "1", which then parses to 1
cast.To[int](`"1"`, cast.Op{cast.DECODE, "json"})              // 1
cast.To[float64](`"1.5"`, cast.Op{cast.DECODE, "json"})        // 1.5
cast.To[bool](`"true"`, cast.Op{cast.DECODE, "json"})          // true

// DECODE=json for slices: decode a JSON array/object string before converting
cast.To[[]int](`[1, 2, 3]`, cast.Op{cast.DECODE, "json"})      // []int{1, 2, 3}
cast.To[[]string](`["a","b"]`, cast.Op{cast.DECODE, "json"})   // []string{"a", "b"}

// UNIQUE_VALUES: deduplicate after conversion, preserving first-seen order
cast.ToE[[]int]([]int{1, 2, 1, 3}, cast.Op{cast.UNIQUE_VALUES, true})
// []int{1, 2, 3}

// LENGTH: pre-allocate backing capacity
cast.ToE[[]int]([]string{"1", "2"}, cast.Op{cast.LENGTH, 100})
Maps
// map → map: keys and values are individually cast to the target types
cast.ToE[map[string]int](map[string]string{"a": "1", "b": "2"})
// map[string]int{"a": 1, "b": 2}

// struct → map: exported field names become keys; embedded structs are inlined
type Point struct{ X, Y int }
cast.ToE[map[string]any](Point{X: 3, Y: 4})
// map[string]any{"X": 3, "Y": 4}

// slice/array → map: element indices become keys
cast.ToE[map[int]string]([]string{"a", "b", "c"})
// map[int]string{0: "a", 1: "b", 2: "c"}

// Options
cast.ToE[map[string]any](myStruct,
    cast.Op{cast.PRIVATE, true},             // include unexported fields
    cast.Op{cast.STRICT, true},              // error on unconvertible fields
    cast.Op{cast.DUPLICATE_KEY_ERROR, true}, // error on duplicate keys (map→map)
)
Structs

ToStruct[T] / ToStructE[T] hydrate a struct from a map or another struct. Field matching is case-sensitive on exported field names. Fields with no matching source key retain their zero value.

type Point struct {
    X int
    Y int
}

// From a map: values are cast to each field's type
point, _ := cast.ToStructE[Point](map[string]any{"X": 3, "Y": "4"})
// Point{X: 3, Y: 4}

// From another struct: matched by exported field name; extra source fields are ignored
type Src struct{ X, Y, Z int }
pointFromStruct, _ := cast.ToStructE[Point](Src{X: 10, Y: 20, Z: 30})
// Point{X: 10, Y: 20}  — Z ignored, no matching field

// STRICT: error when source has keys with no matching target field
_, strictErr := cast.ToStructE[Point](
    map[string]any{"X": 1, "Y": 2, "Z": 3},
    cast.Op{cast.STRICT, true},
)
// strictErr != nil — "Z" has no matching field in Point

// Also accessible via the standard To[T] / ToE[T] entry points
pointFromMap := cast.To[Point](map[string]any{"X": 5, "Y": 6})
// Point{X: 5, Y: 6}
Field key resolution

The lookup key for each target field follows this priority: a cast: struct tag first, then the name portion of a json: tag (options like omitempty are stripped), then the bare field name. A tag value of "-" skips the field entirely. The same resolution applies when the source is another struct.

type Config struct {
    Host string `cast:"host"`           // matched by key "host"
    Port int    `json:"port,omitempty"` // matched by key "port"
    Skip string `cast:"-"`             // never populated from any source
}
cfg, _ := cast.ToStructE[Config](map[string]any{"host": "localhost", "port": 8080})
// Config{Host: "localhost", Port: 8080}

// Struct source: source field keys follow the same tag priority
type Src struct {
    Host string `cast:"host"`
    Port int    `json:"port"`
}
cfg2, _ := cast.ToStructE[Config](Src{Host: "db", Port: 5432})
// Config{Host: "db", Port: 5432}
Unexported fields (PRIVATE)

Unexported fields are skipped in both the source and target by default. Set PRIVATE to read unexported source fields and hydrate unexported target fields.

type connConfig struct {
    host string
    port int
}

// From a map
conn, _ := cast.ToStructE[connConfig](
    map[string]any{"host": "localhost", "port": 8080},
    cast.Op{cast.PRIVATE, true},
)
// connConfig{host: "localhost", port: 8080}

// From a struct with unexported source fields
type Src struct{ host string; port int }
conn2, _ := cast.ToStructE[connConfig](Src{host: "db", port: 5432}, cast.Op{cast.PRIVATE, true})
// connConfig{host: "db", port: 5432}
Channels and Funcs

chan T returns a buffered channel (buffer size 1) pre-loaded with the cast value. cast.Func[T] returns a func() T closure.

// Channel
ten := <-cast.To[chan int]("10")  // 10

// Custom buffer size (≥ 1)
intCh, _ := cast.ToE[chan int](42, cast.Op{cast.LENGTH, 10})

// Func: cast.Func[T] is a named type (type Func[T] func() T);
// a named type is required because Go generics do not accept plain function literals
// as type parameters.
intFunc := cast.To[cast.Func[int]]("10")
fmt.Println(intFunc()) // 10

// Nested composite types are supported
sliceCh    := cast.To[chan []int]([]int{1, 2, 3}) // chan []int
nestedFunc := cast.To[cast.Func[chan int]](42)    // func() chan int
Named types

time.Time, time.Duration, net.IP, *url.URL, *regexp.Regexp, *big.Int, and *big.Float are direct cast targets via To[T] / ToE[T]. See Named-type targets for the full source compatibility matrix.

// time.Time — string (19 formats tried), int/uint (Unix seconds), float (Unix seconds)
rfcTime, _    := cast.ToE[time.Time]("2024-04-22T12:00:00Z") // RFC3339
dateTime, _   := cast.ToE[time.Time]("2024-04-22")           // DateOnly
unixSecTime   := cast.To[time.Time](int64(1713787200))        // Unix seconds
unixSecTime2  := cast.To[time.Time](float64(1713787200.5))    // Unix seconds (fractional)

// time.Duration — time.ParseDuration syntax, or int/float as nanoseconds
duration, _  := cast.ToE[time.Duration]("1h30m45s")
fiveSeconds  := cast.To[time.Duration](int64(5000000000)) // 5s (as ns)

// net.IP — string (IPv4 or IPv6), uint32 (packed IPv4), []byte (4 or 16 bytes)
localIP, _  := cast.ToE[net.IP]("192.168.1.1")
packedIP    := cast.To[net.IP](uint32(0xC0A80101)) // 192.168.1.1 packed

// *url.URL
pageURL, _ := cast.ToE[*url.URL]("https://example.com/path?q=1")

// *regexp.Regexp
fooPattern, _ := cast.ToE[*regexp.Regexp](`^foo\d+$`)

// *big.Int — string auto-detects base: 0x hex, 0o octal, 0b binary, decimal
hexInt, _    := cast.ToE[*big.Int]("0xFF")      // 255
binaryInt, _ := cast.ToE[*big.Int]("0b1010")    // 10
bigFromInt   := cast.To[*big.Int](int64(12345))

// *big.Float — arbitrary precision; float64 sources are limited to float64 precision
bigPi, _ := cast.ToE[*big.Float]("3.14159265358979323846264338327")
Interface targets

error, fmt.Stringer, and github.com/bdlm/std/v2/errors.Error can be cast targets, but only when the source already implements the interface. The value is returned as-is; no string parsing occurs.

myErr := fmt.Errorf("something failed")
cast.To[error](myErr)      // returns myErr unchanged
cast.ToE[error](myErr)     // (myErr, nil)
cast.ToE[error](42)        // (nil, error) — int does not implement error
cast.ToE[fmt.Stringer](42) // (nil, error) — int does not implement fmt.Stringer
Error handling

To drops errors and returns the zero value. ToE returns both the value and the error. Use DEFAULT to substitute a custom fallback on error.

cast.To[int]("Hi!")                 // 0  — error silently dropped
result, err := cast.ToE[int]("Hi!") // 0, error: unable to cast "Hi!" of type string to int

// DEFAULT: return a custom value instead of zero on error
result := cast.To[int]("Hi!", cast.Op{cast.DEFAULT, -1})
// result = -1

Documentation

Overview

Package cast provides generic type conversion for Go 1.21+.

The two public entry points are To (ignores errors) and ToE (returns errors). Both accept an optional variadic Op list that controls conversion behavior; see Flag for available options.

Supported target types are described by the Types constraint: all basic scalar types, slices of scalars, channels of scalars/slices, maps, and Func wrappers for each of those groups.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	Error                    = fmt.Errorf("unable to cast value")
	ErrorSignedToUnsigned    = fmt.Errorf("cannot cast signed value to unsigned integer")
	ErrorInvalidOption       = "invalid %s value '%v'"
	ErrorStrErrorCastingFunc = "error casting %T to %T during function generation"
	ErrorStrUnableToCast     = "unable to cast %#.10v of type %T to %T"
)

Sentinel errors and reusable format strings used throughout the package.

Functions

func To

func To[TTo Types](v any, o ...Op) TTo

To casts the value v to the given type, ignoring any errors. See the ToE documentation more information.

Example (Chan)
ch := cast.To[chan int]("10")
v := <-ch
fmt.Printf("%v (%T)", v, v)
Output:
10 (int)
Example (Float64)
v := cast.To[float64]("1.234")
fmt.Printf("%#v (%T)", v, v)
Output:
1.234 (float64)
Example (Func)
f := cast.To[cast.Func[int]]("10")
fmt.Printf("%v (%T)", f(), f())
Output:
10 (int)
Example (Int)
v := cast.To[int]("1")
fmt.Printf("%#v (%T)", v, v)
Output:
1 (int)
Example (Slice)
v := cast.To[[]int]([]string{"1", "2", "3"})
fmt.Printf("%v (%T)", v, v)
Output:
[1 2 3] ([]int)
Example (String)
v := cast.To[string](1.234)
fmt.Printf("%#v (%T)", v, v)
Output:
"1.234" (string)

func ToE

func ToE[TTo Types](val any, ops ...Op) (panicTo TTo, panicErr error)

ToE casts the value v to the given type, returning any errors.

ops (Ops) is an optional parameter providing flags that can be used to modify the default type conversion behavior. If ops is not provided, the default conversion behavior for a given type is used. Available options depend on the target type, see the documentation for the specific type conversion function for more information.

Complex types have specific default behaviors, for example:

  • If the target type is a channel, a channel with a buffer of 1 is created and the cast value `v` is added to the the channel before it is returned.

  • If the target type is a slice, a slice is created. To pre-allocate backing capacity set the LENGTH flag: `cast.ToE[[]int](v, cast.Op{cast.LENGTH, 10})`. The source must itself be a slice or array; scalar sources are rejected.

  • If the target type is a map, the source is converted into the target map type. Supported sources: map (key/value types cast), struct or *struct (field names become keys), slice or array (indices become keys).

See the documentation for the specific type conversion function for more information.

Example (Chan_length)
ch, e := cast.ToE[chan int](10, cast.Op{cast.LENGTH, 5})
v := <-ch
fmt.Printf("%v (cap %d), %v", v, cap(ch), e)
Output:
10 (cap 5), <nil>
Example (Error_with_default)
v, e := cast.ToE[int]("Hi!", cast.Op{cast.DEFAULT, 10})
fmt.Printf("%#v (%T), %v", v, v, e)
Output:
10 (int), strconv.ParseFloat: parsing "Hi!": invalid syntax: strconv.ParseFloat: parsing "Hi!": invalid syntax: unable to cast "Hi!" of type string to int
Example (Float64)
v, e := cast.ToE[float64]("1")
fmt.Printf("%#v (%T), %v", v, v, e)
Output:
1 (float64), <nil>
Example (Int)
v, e := cast.ToE[int]("1")
fmt.Printf("%#v (%T), %v", v, v, e)
Output:
1 (int), <nil>
Example (Map_from_map)
m, e := cast.ToE[map[string]int](map[string]string{"a": "1"})
fmt.Printf("%v (%T), %v", m["a"], m["a"], e)
Output:
1 (int), <nil>
Example (Map_from_private_struct)
type Point struct{ x, y int }
m, e := cast.ToE[map[string]any](Point{x: 3, y: 4}, cast.Op{cast.PRIVATE, true})
fmt.Printf("x=%v y=%v, %v", m["x"], m["y"], e)
Output:
x=3 y=4, <nil>
Example (Map_from_slice)
m, e := cast.ToE[map[int]string]([]string{"a", "b", "c"})
fmt.Printf("%v (%T), %v", m[0], m[0], e)
Output:
a (string), <nil>
Example (Map_from_struct)
type Point struct{ X, Y int }
m, e := cast.ToE[map[string]any](Point{X: 3, Y: 4})
fmt.Printf("X=%v Y=%v, %v", m["X"], m["Y"], e)
Output:
X=3 Y=4, <nil>
Example (Map_to_struct)
type MyStruct struct {
	X int
	Y int
	A string
	B string
}
yourData := map[string]string{
	"X": "3",
	"Y": "4",
	"A": "hello",
	"B": "world",
}
p, e := cast.ToE[MyStruct](yourData)
fmt.Printf(
	"p=(%T), X=%v (%T), Y=%v (%T), A=%v (%T), B=%v (%T), %v",
	p, p.X, p.X, p.Y, p.Y, p.A, p.A, p.B, p.B, e,
)
Output:
p=(cast_test.MyStruct), X=3 (int), Y=4 (int), A=hello (string), B=world (string), <nil>
Example (Map_to_struct_tags)
type MyStruct struct {
	X int    `cast:"field_x"`
	Y int    `cast:"field_y"`
	A string `cast:"field_a"`
	B string `cast:"field_b"`
}
yourData := map[string]string{
	"field_x": "3",
	"field_y": "4",
	"field_a": "hello",
	"field_b": "world",
}
p, e := cast.ToE[MyStruct](yourData)
fmt.Printf("p=(%T), X=%v (%T), Y=%v (%T), A=%v (%T), B=%v (%T), %v", p, p.X, p.X, p.Y, p.Y, p.A, p.A, p.B, p.B, e)
Output:
p=(cast_test.MyStruct), X=3 (int), Y=4 (int), A=hello (string), B=world (string), <nil>
Example (Slice_unique_values)
v, e := cast.ToE[[]int]([]int{1, 2, 1, 3}, cast.Op{cast.UNIQUE_VALUES, true})
fmt.Printf("%v (%T), %v", v, v, e)
Output:
[1 2 3] ([]int), <nil>
Example (String)
v, e := cast.ToE[string](float64(1.0))
fmt.Printf("%#v (%T), %v", v, v, e)
Output:
"1" (string), <nil>
Example (String_json)
v, e := cast.ToE[string](`hello "world"`, cast.Op{cast.JSON, true})
fmt.Printf("%v, %v", v, e)
Output:
"hello \"world\"", <nil>
Example (Struct_to_map)
type YourStruct struct {
	X int
	Y int
	A string
	B string
}
yourStruct := YourStruct{
	X: 3,
	Y: 4,
	A: "hello",
	B: "world",
}
p, e := cast.ToE[map[string]string](yourStruct)
fmt.Printf(
	"p=(%T), X=%v (%T), Y=%v (%T), A=%v (%T), B=%v (%T), %v",
	p, p["X"], p["X"], p["Y"], p["Y"], p["A"], p["A"], p["B"], p["B"], e,
)
Output:
p=(map[string]string), X=3 (string), Y=4 (string), A=hello (string), B=world (string), <nil>
Example (Struct_to_map_error)
type YourStruct struct {
	X int
	Y int
	A string
	B string
}
yourStruct := YourStruct{
	X: 3,
	Y: 4,
	A: "hello",
	B: "world",
}
p, e := cast.ToE[map[string]int](yourStruct)
fmt.Printf(
	"p=(%T), X=%v (%T), Y=%v (%T), A=%v (%T), B=%v (%T), %v",
	p, p["X"], p["X"], p["Y"], p["Y"], p["A"], p["A"], p["B"], p["B"], e,
)
Output:
p=(map[string]int), X=3 (int), Y=4 (int), A=0 (int), B=0 (int), <nil>
Example (Struct_to_struct)
type MyStruct struct {
	X int
	Y int
	A string
	B string
}
type YourStruct struct {
	X string
	Y string
	A string
	B string
}
yourStruct := YourStruct{
	X: "3",
	Y: "4",
	A: "hello",
	B: "world",
}
p, e := cast.ToE[MyStruct](yourStruct, cast.Op{cast.PRIVATE, true})
fmt.Printf("p=(%T), X=%v (%T), Y=%v (%T), A=%v (%T), B=%v (%T), %v", p, p.X, p.X, p.Y, p.Y, p.A, p.A, p.B, p.B, e)
Output:
p=(cast_test.MyStruct), X=3 (int), Y=4 (int), A=hello (string), B=world (string), <nil>
Example (Struct_to_struct_json_tags)
type MyStruct struct {
	X int    `json:"fieldX"`
	Y int    `json:"fieldY"`
	a string `json:"fieldA"`
	b string `json:"fieldB"`
}
type YourStruct struct {
	fieldX string
	fieldY string
	fieldA string
	fieldB string
}
yourStruct := YourStruct{
	fieldX: "3",
	fieldY: "4",
	fieldA: "hello",
	fieldB: "world",
}
p, e := cast.ToE[MyStruct](yourStruct, cast.Op{cast.PRIVATE, true})
fmt.Printf("p=(%T), X=%v (%T), Y=%v (%T), a=%v (%T), b=%v (%T), %v", p, p.X, p.X, p.Y, p.Y, p.a, p.a, p.b, p.b, e)
Output:
p=(cast_test.MyStruct), X=3 (int), Y=4 (int), a=hello (string), b=world (string), <nil>
Example (Struct_to_struct_private)
type MyStruct struct {
	X int
	Y int
	a string
	b string
}
type YourStruct struct {
	X string
	Y string
	a string
	b string
}
yourStruct := YourStruct{
	X: "3",
	Y: "4",
	a: "hello",
	b: "world",
}
p, e := cast.ToE[MyStruct](yourStruct, cast.Op{cast.PRIVATE, true})
fmt.Printf("p=(%T), X=%v (%T), Y=%v (%T), a=%v (%T), b=%v (%T), %v", p, p.X, p.X, p.Y, p.Y, p.a, p.a, p.b, p.b, e)
Output:
p=(cast_test.MyStruct), X=3 (int), Y=4 (int), a=hello (string), b=world (string), <nil>
Example (Uint_abs)
v, e := cast.ToE[uint]("-1", cast.Op{cast.ABS, true})
fmt.Printf("%v (%T), %v", v, v, e)
Output:
1 (uint), <nil>
Example (Uint_err)
v, e := cast.ToE[uint]("-1")
fmt.Printf("%v (%T), %v", v, v, e)
Output:
0 (uint), cannot cast signed value to unsigned integer: unable to cast "-1" of type string to uint

func ToStruct added in v2.1.1

func ToStruct[T any](from any, ops ...Op) T

ToStruct casts from into a struct of type T, ignoring errors. See ToStructE for full documentation.

func ToStructE added in v2.1.1

func ToStructE[T any](from any, ops ...Op) (T, error)

ToStructE casts from into a struct of type T, returning any errors.

T must be a struct type. The source may be a map with keys convertible to string, or another struct whose field names overlap with T.

Field matching is case-sensitive and tag-aware. The lookup key for each target field is resolved by checking a cast tag first, then a json tag, then the field name. For each target field in T:

  • If the source has no matching key, the field retains its zero value (or returns an error when STRICT is set).
  • If the source value cannot be cast to the field type, the field is skipped (or returns an error when STRICT is set).

Options:

  • DEFAULT: T, value to return on error.
  • PRIVATE: bool, include unexported fields in source collection and target hydration.
  • STRICT: bool, error on unknown source keys or unconvertible field values.

Types

type Flag added in v2.0.1

type Flag int

Flag is the key type for conversion options passed to To and ToE.

const (
	DEFAULT Flag = iota // TTo,  LOCAL  — value to return on error; type-specific, not passed to nested casts

	ABS                 // bool,   GLOBAL — use absolute value during uint conversion
	DECODE              // string, LOCAL  — decode string source before conversion; only applies to string/error/Stringer sources; supported values: "JSON"/"json"
	DUPLICATE_KEY_ERROR // bool,   LOCAL  — error on duplicate key (map→map only); not meaningful in nested casts
	FORMAT              // string, GLOBAL — Format string for time/duration parsing
	JSON                // bool,   GLOBAL — encode strings as JSON
	LENGTH              // int,    GLOBAL — initial capacity for slices / buffer size for channels; applies to all slice and chan targets in the tree (slices allow 0; channels require >= 1)
	PRIVATE             // bool,   GLOBAL — include unexported struct fields in map output
	STRICT              // bool,   GLOBAL — return error instead of skipping unconvertible fields
	UNIQUE_VALUES       // bool,   GLOBAL — dedupe slice values; applies to all slice targets in the tree
)

Available option flags. Flags fall into two categories:

Global flags propagate through the full conversion tree and are preserved by [ops.Global]. They apply the same way at every level of a nested conversion:

  • ABS, JSON, LENGTH, PRIVATE, STRICT, UNIQUE_VALUES

Local flags apply only to the conversion they are passed to and are stripped by [ops.Global]. Container converters read their own local flags, then pass [ops.Global] to element-level casts so the local flags do not leak into nested conversions where they carry no meaning or the wrong type:

  • DEFAULT, DUPLICATE_KEY_ERROR

type Func

type Func[TTo Types] func() TTo

Func is a named zero-argument function type that returns a T. A named type is required because Go generics cannot use plain function literals as type parameters directly.

type Op added in v2.0.1

type Op struct {
	Flag Flag
	Val  any
}

Op is a single key/value option passed to To or ToE. Build one with a Flag constant and the appropriate value type for that flag.

type Tbase

type Tbase interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
		~float32 | ~float64 |
		~complex64 | ~complex128 |
		~string | ~bool |
		any
}

Tbase covers all scalar types. The any term makes this constraint effectively unconstrained — all types satisfy it. This is intentional: interface targets like error and fmt.Stringer need to be expressible as TTo, and there is no way to enumerate all interface types. Unsupported kinds (struct, pointer, etc.) are rejected at runtime by ToE's dispatch switch.

type Tchan

type Tchan interface {
	~chan Tbase |
		~chan []int | ~chan []int8 | ~chan []int16 | ~chan []int32 | ~chan []int64 |
		~chan []uint | ~chan []uint8 | ~chan []uint16 | ~chan []uint32 | ~chan []uint64 | ~chan []uintptr |
		~chan []float32 | ~chan []float64 |
		~chan []complex64 | ~chan []complex128 |
		~chan []string | ~chan []bool |
		~chan []any | ~chan Func[Tbase] |
		~chan chan Tbase
}

Tchan covers channels of scalars (~chan Tbase covers all basic-type channels in one term), channels of slices, channels of Func values, and nested channels (chan chan T).

type Tmap

type Tmap interface {
	~map[Tbase]Tbase |
		~map[Tbase][]int |
		~map[Tbase][]int8 |
		~map[Tbase][]int16 |
		~map[Tbase][]int32 |
		~map[Tbase][]int64 |
		~map[Tbase][]uint |
		~map[Tbase][]uint8 |
		~map[Tbase][]uint16 |
		~map[Tbase][]uint32 |
		~map[Tbase][]uint64 |
		~map[Tbase][]uintptr |
		~map[Tbase][]float32 |
		~map[Tbase][]float64 |
		~map[Tbase][]complex64 |
		~map[Tbase][]complex128 |
		~map[Tbase][]string |
		~map[Tbase][]bool |
		~map[Tbase][]any |
		~map[Tbase][]Func[Tbase]
}

type Tslice

type Tslice interface {
	~[]int | ~[]int8 | ~[]int16 | ~[]int32 | ~[]int64 |
		~[]uint | ~[]uint8 | ~[]uint16 | ~[]uint32 | ~[]uint64 | ~[]uintptr |
		~[]float32 | ~[]float64 |
		~[]complex64 | ~[]complex128 |
		~[]string | ~[]bool |
		~[]any
}

Tslice covers slice types of every scalar element kind, plus named types with the same underlying slice type (e.g. type Tags []string).

type Types

type Types interface {
	Tbase | Tslice | Tchan | Tmap | Func[Tbase] |
		Func[[]int] | Func[[]int8] | Func[[]int16] | Func[[]int32] | Func[[]int64] |
		Func[[]uint] | Func[[]uint8] | Func[[]uint16] | Func[[]uint32] | Func[[]uint64] | Func[[]uintptr] |
		Func[[]float32] | Func[[]float64] |
		Func[[]complex64] | Func[[]complex128] |
		Func[[]string] | Func[[]bool] | Func[[]any] |
		Func[chan int] | Func[chan int8] | Func[chan int16] | Func[chan int32] | Func[chan int64] |
		Func[chan uint] | Func[chan uint8] | Func[chan uint16] | Func[chan uint32] | Func[chan uint64] | Func[chan uintptr] |
		Func[chan float32] | Func[chan float64] |
		Func[chan complex64] | Func[chan complex128] |
		Func[chan string] | Func[chan bool] | Func[chan any]
}

Types is the top-level constraint that To and ToE accept as TTo. It unions all supported target categories. Func variants for slices and channels are enumerated explicitly because Go does not expand Func[Tslice] into all individual Func[[]T] terms automatically.

Jump to

Keyboard shortcuts

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