Skip to content

Unions

Combine unrelated types into canonical anonymous closed alternatives.

Updated View as Markdown

A | B is an anonymous closed union of types.

fn decode(input: string): int | string {
    if input == "answer" {
        return 42
    }
    return input
}

Unlike a named enum, the alternatives are the member types themselves.

Injection

A value of a member type can flow into an expected union.

value: int | string = 42

This is a checked union injection. It does not make unrelated types generally assignable to one another.

Branch inference can synthesize a union when normally completing branches produce incompatible closed types.

fn choose(flag: bool) {
    if flag { 1 } else { "one" }
}

State an explicit return type when that union is part of a public API.

Canonicalization

Anonymous unions are structural and canonicalized:

  • member order does not create a different type;
  • duplicate members collapse;
  • nested anonymous unions flatten.

Conceptually, A | B, B | A, and A | (B | A) describe the same union.

Matching

For nominal member types, use type-name patterns.

struct NumberValue { value: int }
struct TextValue { value: string }

fn show(value: NumberValue | TextValue): string {
    return match value {
        NumberValue(number) => "${number.value}"
        TextValue(text) => text.value
    }
}

For scalar or other type groups, bind the value with name: Type.

fn describe(value: int | string): string {
    return match value {
        number: int => "number: $number"
        text: string => "text: $text"
    }
}

The match checker knows the union’s complete member set.

Shared fields

Field access can be valid across a union when every constituent exposes a compatible field.

struct Person { id: int, name: string }
struct Device { id: int, serial: string }

fn identifier(value: Person | Device): int {
    return value.id
}

If any member lacks the field or gives it an incompatible type, direct access is rejected. Match the alternatives instead.

Method dispatch across a union similarly requires every constituent to support a compatible method.

Distinct unions

Two anonymous unions with the same member set are the same structural type. Wrap one in a distinct alias when domain identity must remain separate.

distinct type InputValue = int | string
distinct type OutputValue = int | string

The two aliases no longer interchange implicitly even though their representations use the same member types.

Choosing enums

Use a named enum when:

  • alternatives need stable variant names;
  • several alternatives use the same payload type;
  • payload fields need variant-specific names;
  • behavior or derives belong to the sum type;
  • the domain should remain distinct from another structurally equal set.

Use an anonymous union for a local closed combination of already meaningful types.

Option[T], Result[T, E], and declared error unions have additional construction and propagation rules and are documented under Errors.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close