Skip to main content
Tolk supports union types T1 | T2 | ... similar to TypeScript. They allow a value to belong to one of several possible types. Pattern matching over unions is essential for message handling. A special case T | null is written as T? and called “nullable”.

Not only structures: arbitrary types

All these types are valid:
  • int | slice
  • address | Point | null
  • Increment | Reset | coins
  • int8 | int16 | int32 | int64
Union types are automatically flattened:
Union types support assignment based on subtype relations. For instance, B | C can be passed/assigned to A | B | C | D:

match must cover all cases

In other words, it must be exhaustive.
match can be used for nullable types, since T? is T | null. It may also be used as an expression:
See pattern matching for syntax details.

Auto-inference of a union results in an error

What if match arms result in different types, what is the resulting type?
Formally, the type of a is inferred as builder | int, but this is most likely not what is intended and typically indicates an error in the code. In such situations, the compiler emits a message:
So, either explicitly declare a as a union, or fix contract’s code if it’s a misprint. The same applies to other situations:
The result is inferred as int32 | int64, which is valid, but in most cases a single integer type is expected. The compiler shows an error, just explicitly declare a return type:
Anyway, declaring return types is good practice, and following it resolves any ambiguity.

Operators is and !is

Besides match, unions can also be tested using is:

Lazy match for unions

In all examples of message handling, unions are parsed with lazy:
This pattern is called “lazy match”:
  1. No union is allocated on the stack upfront; loading is deferred until needed.
  2. match operates by inspecting the slice prefix (opcode), not by typeid on the stack.
This approach is significantly more efficient, although unions continue to function correctly without lazy and comply with all type-system rules. Read about lazy loading.

Stack layout and serialization

Unions have a complex stack layout, commonly named as “tagged unions”. Enums in Rust work the same way. Serialization depends on whether the it’s a union of structures with manual serialization prefixes:
  • if yes (struct (0x1234) A), those prefixes are used
  • if no, the compiler auto-generates a prefix tree; for instance, T1 | T2 is called “Either”: ‘0’+T1 or ‘1’+T2
For details, follow TVM representation and Serialization.