2.19. 变体
变体是无名类型,它们支持可以是许多命名 case 之一的值,每个 case 可能具有不同的值和类型:
var t : variant<i_value:uint,f_value:float>
有一个简写类型 alias 语法来定义 variant:
variant U_F {
i_value : uint
f_value : float
}
typedef U_F = variant<i_value:uint,f_value:float> // 与上述声明完全相同
如果任意两个变体具有相同类型的相同命名个案,则它们属于同一类型,并且顺序相同。
变体保存当前 case 的 index
,以及仅当前 case 的值。
当前的大小写选择可以通过 is
运算符检查,并通过 as
运算符访问:
assert(t is i_value)
assert(t as i_value == 0x3f800000)
可以通过复制不同 case 的正确构造的变体来修改整个变体选择:
t = U_F(i_value = 0x40000000) // 现在是 i_value
t = U_F(f_value = 1.0) // 现在是 f_value
访问类型不正确的 variant case 将导致 panic:
t = U_F(i_value = 0x40000000)
return t as f_value // panic, 无效的变体索引
可通过``?as``作进行安全导航:
return t ?as f_value ?? 1.0 // 如果 t 不f_value,将返回 1.0
也可以以不安全的方式访问 Cases,而无需检查类型:
unsafe {
t.i_value = 0x3f800000
return t.f_value // 将返回 f_value 占用的内存 - 即 1.0f
}
当前索引可以通过 variant_index
函数来确定:
var t : U_F
assert(variant_index(t)==0)
特定情况的索引值可以通过 variant_index
和 safe_variant_index
类型特征来确定。
对于无效的索引和类型,safe_variant_index
将返回 -1,而 variant_index
将报告编译错误:
assert(typeinfo(variant_index<i_value> t)==0)
assert(typeinfo(variant_index<f_value> t)==1)
assert(typeinfo(variant_index<unknown_value> t)==-1) // compilation error
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)
当前 case 选择可以通过不安全作 safe_variant_index
来修改:
unsafe
set_variant_index(t, typeinfo variant_index<f_value>(t))
2.19.1. 对齐和数据布局
变体包含当前 case 的“索引”,后跟各个 case 的并集,类似于以下 C++ 布局:
struct MyVariantName {
int32_t __variant_index;
union {
type0 case0;
type1 case1;
...
};
};
单个个案从相同的偏移量开始。
variant 类型通过其最大大小写的对齐方式进行对齐,但不小于 int32 的对齐方式。