Skip to content

Conversation

@userzhy
Copy link
Contributor

@userzhy userzhy commented Dec 19, 2025

Why?

Go Fory lacks support for Union types, which prevents cross-language serialization compatibility with other languages like C++, Java, Python, and Rust that already support union/variant types. This limitation was reported in issue #3031.

Without Union type support, Go users cannot:

  • Serialize union types to communicate with other languages
  • Use tagged union patterns common in serialization scenarios
  • Leverage the full xlang serialization capabilities

What does this PR do?

This PR implements complete Union type support for Go Fory by:

  1. Added TypeId constants to go/fory/types.go:

    • UNION = 38 - for tagged union types
    • NONE = 39 - for empty/unit values
  2. Implemented Union struct and unionSerializer in go/fory/union.go:

    • Union struct that holds an interface{} value
    • Serializes Union types by writing variant index + value
    • Supports both internal Go mode and xlang mode (with type info)
    • Properly dispatches to alternative type serializers
  3. Added RegisterUnionType API to Fory:

    • Allows users to register alternative types for a union
    • Example: f.RegisterUnionType(reflect.TypeOf(int32(0)), reflect.TypeOf(""))
  4. Added comprehensive tests in go/fory/union_test.go:

    • Basic types (int32, string, float64)
    • Multiple alternative types
    • Null/nil values
    • Pointer types
    • Bytes type
    • Reference tracking mode
    • Error cases (invalid alternatives, empty registration)

The implementation follows the same binary protocol as C++/Python/Rust variant serialization:

  1. Write variant index (varuint32)
  2. In xlang mode, write type info for the active alternative
  3. Write the value using the alternative's serializer

Related issues

Fixes #3031
Related to #3027 (tracking issue for xlang union type system)

Does this PR introduce any user-facing change?

  • Does this PR introduce any public API change?

    • Yes: Adds Union struct and RegisterUnionType method. Users can now use Union types in their code and they will be automatically serialized/deserialized.
    • This is backward compatible - existing code continues to work.
  • Does this PR introduce any binary protocol compatibility change?

    • No: The implementation uses the existing TypeId.UNION (38) and TypeId.NONE (39) defined in the specification.
    • The binary protocol follows the same format as C++/Python/Rust implementations.
    • Existing serialized data is not affected.

Benchmark

This PR does not have a performance impact on existing functionality:

  • Union type handling only activates when Union types are actually used
  • The implementation uses efficient type matching via reflection
  • Variant index is encoded using varuint32 (compact encoding)
  • No changes to existing serializers or hot paths

For Union types specifically, the overhead is minimal:

  • One varuint32 write/read for the variant index
  • One type dispatch (same as normal polymorphic serialization)
  • No additional allocations beyond the Union struct itself
@userzhy userzhy requested a review from chaokunyang as a code owner December 19, 2025 14:50
Add support for tagged union types in Go Fory, enabling cross-language
serialization compatibility with other languages like C++, Java, Python,
and Rust that already support union/variant types.

Changes:
- Add UNION (38) and NONE (39) type constants to types.go
- Implement Union struct and unionSerializer in union.go
- Add RegisterUnionType API to Fory for registering union alternatives
- Add comprehensive tests in union_test.go

The implementation follows the same binary protocol as other languages:
1. Write variant index (varuint32)
2. In xlang mode, write type info for the active alternative
3. Write the value data using the alternative's serializer

Fixes apache#3031
Address review feedback from chaokunyang. Since Go 1.23+ is used,
replace interface{}-based Union with generic Union2, Union3, Union4 types.

New API:
- Union2[T1, T2], Union3[T1, T2, T3], Union4[T1, T2, T3, T4] generic types
- NewUnion2A/B, NewUnion3A/B/C, NewUnion4A/B/C/D constructors
- Match() methods for pattern matching (type-safe callbacks)
- Index(), IsFirst(), IsSecond() accessor methods
- First(), Second() value extraction methods (panics on wrong access)
- RegisterUnion2Type, RegisterUnion3Type, RegisterUnion4Type functions

Benefits:
- Compile-time type safety
- Explicit variant tracking
- Pattern matching support
- No runtime type assertions needed
@userzhy
Copy link
Contributor Author

userzhy commented Dec 24, 2025

@chaokunyang Fixed, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants