有些使用接口的场景,不一定要全部用接口来实现,使用联合类型的代码看起来更简洁,清晰

    相对于go和rust来说,联合类型给V加分不少,用起来很舒服

    语法类似typescript,使用type 和 | 来定义一个联合类型

    使用pub关键字,定义公共的联合类型

    使用场景

    联合类型相对于接口来说,比较适合于一个类型是已知的几种类型的其中一种,已知类型的数量是有限的,固定的,相对封闭的,不需要考虑未知类型的扩展性

    1. //代码位置:vlib/x/json2/decoder.v
    2. pub type Any = string | int | i64 | f32 | f64 | any_int | any_float | bool | Null | []Any | map[string]Any

    还有另一个最大的场景就是V编译器自身,使用了联合类型来包含所有的AST抽象语法树的类型,毕竟AST的节点类型也是有限的,固定的,相对封闭的,后续AST的逻辑代码也变得简洁清晰很多

    1. //代码位置:vlib/v/ast/ast.v
    2. pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl | UnionSumTypeDecl
    3. pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | AtExpr | BoolLiteral | CTempVar |
    4. CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal |
    5. FloatLiteral | Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral |
    6. Likely | LockExpr | MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr |
    7. RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral |
    8. StructInit | Type | TypeOf | UnsafeExpr
    9. pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | DeferStmt |
    10. EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt |
    11. GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | Return | SqlStmt |
    12. StructDecl | TypeDecl

    而接口更适合用来支持未知类型的扩展性

    使用联合类型

    • 联合类型作为函数的参数或返回值,也可以作为变量声明,结构体字段
    • 使用match语句,进行类型的进一步判断
    • 使用match语句,如果需要修改联合类型的值,需要在变量前加mut
    • 使用is关键字联合类型具体是哪一种类型
    • 使用as关键字将联合类型转换为另一种类型,当然要转换的类型在联合类型定义的类型范围内

    获取联合类型的具体类型

    联合类型使用内置方法type_name()来返回变量的具体类型

    1. module main
    2. struct Point {
    3. x int
    4. }
    5. type MySumType = Point | f32 | int
    6. fn main() {
    7. // 联合类型使用type_name()
    8. sa := MySumType(32)
    9. sb := MySumType(f32(123.0))
    10. sc := MySumType(Point{
    11. x: 43
    12. })
    13. println(sa.type_name()) // int
    14. println(sb.type_name()) // f32
    15. println(sc.type_name()) // Point
    16. }
    1. type Str = string | ustring
    2. struct Foo {
    3. v int
    4. }
    5. struct Bar {
    6. }
    7. type FooBar = Foo | Bar
    8. fn main() {
    9. s1 := Str('s')
    10. s2 := Str('s')
    11. u1 := Str('s'.ustring())
    12. u2 := Str('s'.ustring())
    13. println( s1 == s1 ) //联合类型判断相等或不等:同类型,同值
    14. println( s1 == s2 )
    15. println( u1 == u2 )
    16. //类型不同,值相同也不等
    17. foo := FooBar( Foo{v: 0} )
    18. bar := FooBar( Bar{v: 0} )
    19. println( foo.v == bar.v )
    20. println( foo != bar )
    21. }

    for is类型循环判断

    用于联合类型的类型循环判断(感觉没啥用,就是一个语法糖而已)

    1. module main
    2. struct Milk {
    3. mut:
    4. name string
    5. }
    6. struct Eggs {
    7. mut:
    8. name string
    9. }
    10. type Food = Eggs | Milk
    11. fn main() {
    12. mut f := Food(Eggs{'test'})
    13. //不带mut
    14. for f is Eggs {
    15. println(typeof(f).name)
    16. break
    17. }
    18. //等价于
    19. for {
    20. if f is Eggs {
    21. println(typeof(f).name)
    22. break
    23. }
    24. }
    25. //带mut
    26. for mut f is Eggs {
    27. f.name = 'eggs'
    28. println(f.name)
    29. break
    30. }
    31. //等价于
    32. for {
    33. if mut f is Eggs {
    34. f.name = 'eggs'
    35. println(f.name)
    36. break
    37. }
    38. }

    as类型转换

    联合类型嵌套

    联合类型还可以嵌套使用,支持更复杂的场景

    1. struct FnDecl {
    2. pos int
    3. }
    4. struct StructDecl {
    5. pos int
    6. }
    7. struct IfExpr {
    8. pos int
    9. }
    10. struct IntegerLiteral {
    11. val string
    12. }
    13. type Expr = IfExpr | IntegerLiteral
    14. type Stmt = FnDecl | StructDecl
    15. type Node = Expr | Stmt //联合类型嵌套
    1. module main
    2. fn main() {
    3. mut m := Mysumtype{}
    4. m = int(11)
    5. println(m.str())
    6. }
    7. type Mysumtype = int | string
    8. pub fn (mysum Mysumtype) str() string { // 联合类型的方法
    9. return 'from mysumtype'
    10. }

    访问公共字段

    联合类型可以在方法中访问所有类型的公共字段,并且支持联合类型的多级嵌套

    截图中是实际的V编译器中的例子:

    stmt.pos是所有Stmt联合类型的公共字段,有了这个特性以后,代码就可以简洁到像截图中的第一个方法那样

    联合类型作为结构体字段

    结构体字段的类型也可以是联合体类型

    1. struct MyStruct {
    2. x int
    3. }
    4. struct MyStruct2 {
    5. y string
    6. }
    7. type MySumType = MyStruct | MyStruct2
    8. struct Abc {
    9. bar MySumType // bar字段的类型也可以是联合类型
    10. }
    11. fn main() {
    12. x := Abc{
    13. bar: MyStruct{123}
    14. }
    15. if x.bar is MyStruct {
    16. println(x.bar)
    17. }
    18. match x.bar { // match匹配也可以使用
    19. MyStruct { println(x.bar.x) } // 也会自动造型为具体类型
    20. else {}
    21. }

    子类不允许是指针类型