Union Types
C语言
在C语言中,union(联合体)是一种内存复用的方案,即同一片内存可以当作不同的类型来访问。
实际使用中,我们常常不直接用union,而是添加一个字段来表示具体取哪一个,即tagged-union。
c
typedef enum {
KIND_INT,
KIND_FLOAT,
KIND_CHAR,
} MyUnionKind;
typedef struct {
// tag
MyUnionKind kind;
// data
union {
int i;
float f;
char c;
} as;
} MyUnion;这样在实际使用时,可以通过判断tag值来选取具体的数据类型。
c
if (my_union.kind == KIND_INT) {
printf("int: %d\n", my_union.as.i);
} else if (my_union.kind == KIND_FLOAT) {
printf("float: %f\n", my_union.as.f);
} else if (my_union.kind == KIND_CHAR) {
printf("char: %c\n", my_union.as.c);
}也可以通过switch语句来根据tag值取用对应的数据:
C
switch (my_union.kind) {
case KIND_INT:
printf("int: %d\n", my_union.as.i);
break;
case KIND_FLOAT:
printf("float: %f\n", my_union.as.f);
break;
case KIND_CHAR:
printf("char: %c\n", my_union.as.c);
break;
}Rust语言
在Rust语言中,除了与C差不多的原始union类型,还提供了tagged_union的直接支持 即typed enum:
rust
enum MyUnion {
Int(i32),
Float(f32),
Char(char),
}这样在实际使用时,可以通过match语句来根据tag值取用对应的数据:
rust
match my_union {
MyUnion::Int(i) => println!("int: {}", i),
MyUnion::Float(f) => println!("float: {}", f),
MyUnion::Char(c) => println!("char: {}", c),
}注意,这里match的用法相当于C语言里的switch, 但是Rust把tag隐藏在enum的定义中,无法直接获取其tag值。
类似if的用法可以这么写:
rust
if let MyUnion::Int(i) = my_union {
println!("int: {}", i);
} else if let MyUnion::Float(f) = my_union {
println!("float: {}", f);
} else if let MyUnion::Char(c) = my_union {
println!("char: {}", c);
}或者只需要判断单个case的时候:
rust
if matches!(my_union, MyUnion::Int(_)) {
// ...
}Auto语言
Auto语言既要兼容C,也要兼容Rust, 因此既要提供原始的union, 也需要提供tagged union, 并且最好还能保留直接获取tag的途径。
在Auto中,原始的union也使用和C差不多的语法:
auto
union MyUnion {
i int
f float
c char
}Tagged-Union则选用tag关键字:
auto
tag MyTag {
Int int
Float float
Char char
}
这个定义相当于C里用`enum`定义Tag类型,再用`struct`+`union`定义数据类型:
这里前面的`Int`,`Float`和`Char`是tag名称,相当于C里的:
```c
typedef enum {
Int,
Float,
Char,
} MyTagKind;后面的int,float和char是实际的数据类型,相当于:
c
typedef struct {
MyTagKind tag;
union {
int Int;
float Float;
char Char;
} as;
} MyTag;Auto语言的is关键字相当于C的switch或Rust的match:
auto
is my_tag_data {
Int(i) => println!("int: {}", i),
Float(f) => println!("float: {}", f),
Char(c) => println!("char: {}", c),
}可以直接获取tag值:
auto
let t = my_tag_data.tag;这里t的类型是enum MyTag.Tag,编译器会自动添加该枚举类型。
如果需要强制按照某种类型取出数据,可以使用as关键字:
auto
sys {
let i = my_tag_data.as.Int;
}注意,这里的代码是不安全的,应当包含在sys框中。