3.15. Variant
Variants are nameless types which provide support for values that can be one of a number of named cases, possibly each with different values and types:
var t : variant<i_value:uint; f_value:float>
There is a shorthand type alias syntax to define a variant:
variant U_F {
i_value : uint
f_value : float
}
The typedef form declares exactly the same type:
typedef U_F = variant<i_value:uint; f_value:float>
Any two variants are the same type if they have the same named cases of the same types in the same order.
Variants hold the index of the current case, as well as the value for the current case only.
The current case selection can be checked via the is operator, and accessed via the as operator:
t = U_F(i_value = 0x3f800000)
assert(t is i_value)
assert(t as i_value == 0x3f800000)
The entire variant selection can be modified by copying the properly constructed variant of a different case:
t = U_F(i_value = 0x40000000) // now case is i_value
t = U_F(f_value = 1.0) // now case is f_value
Accessing a variant case of the incorrect type will cause a panic:
def read_float(u : U_F) : float {
return u as f_value // panic when the current case is not f_value
}
Safe navigation is available via the ?as operation:
def read_float_or(u : U_F) : float {
return u ?as f_value ?? 1.0 // will return 1.0 if u is not f_value
}
Cases can also be accessed in an unsafe manner without checking the type:
def reinterpret_case(var u : U_F) : float {
unsafe {
u.i_value = 0x3f800000
return u.f_value // returns the memory occupied by f_value - i.e. 1.0f
}
}
The current index can be determined via the variant_index function:
var t : U_F
assert(variant_index(t)==0)
The index value for a specific case can be determined via the variant_index and safe_variant_index type traits.
safe_variant_index will return -1 for invalid indices and types, whereas variant_index will report a compilation error:
assert(typeinfo variant_index<i_value>(t)==0)
assert(typeinfo variant_index<f_value>(t)==1)
assert(typeinfo safe_variant_index<i_value>(t)==0)
assert(typeinfo safe_variant_index<f_value>(t)==1)
assert(typeinfo safe_variant_index<unknown_value>(t)==-1) // -1, no error
variant_index on an unknown case name is a compilation error:
var u : U_F
assert(typeinfo variant_index<unknown_value>(u)==-1) // error[30839]: variant unknown_value not found
Current case selection can be modified with the unsafe operation set_variant_index:
unsafe {
set_variant_index(t, typeinfo variant_index<f_value>(t))
}
3.15.1. Alignment and data layout
Variants contain the ‘index’ of the current case, followed by a union of individual cases, similar to the following C++ layout:
struct MyVariantName {
int32_t __variant_index;
union {
type0 case0;
type1 case1;
...
};
};
Individual cases start from the same offset.
The variant type is aligned by the alignment of its largest case, but no less than that of an int32.
See also
Pattern matching for matching and extracting variant cases,
Aliases for the typedef shorthand variant syntax,
Datatypes for a list of built-in types,
Move, copy, and clone for variant clone and move semantics,
Unsafe for unsafe variant field access and ?as without null coalescing,
Finalizers for variant finalization.