联合类型¶
变量或表达式的类型可以由多种类型组成。这称为联合类型。例如,在不同 if 分支中为同一个变量赋值时
if 1 + 2 == 3
a = 1
else
a = "hello"
end
a # : Int32 | String
在 if 结束时,a
将具有 Int32 | String
类型,读作“Int32 和 String 的并集”。此联合类型由编译器自动创建。在运行时,a
当然只是一种类型。这可以通过调用 class
方法来查看
# The runtime type
a.class # => Int32
可以通过使用 typeof 查看编译时类型
# The compile-time type
typeof(a) # => Int32 | String
一个联合可以包含任意数量的类型。在对类型为联合类型的表达式调用方法时,联合中的所有类型都必须响应该方法,否则会给出编译时错误。方法调用的类型是这些方法返回类型的联合类型。
# to_s is defined for Int32 and String, it returns String
a.to_s # => String
a + 1 # Error, because String#+(Int32) isn't defined
如果需要,可以将变量定义为编译时的联合类型
# set the compile-time type
a = 0.as(Int32 | Nil | String)
typeof(a) # => Int32 | Nil | String
联合类型规则¶
在一般情况下,当组合两个类型 T1
和 T2
时,结果是联合 T1 | T2
。但是,有一些情况会导致结果类型为不同的类型。
相同层次结构下类和结构的联合¶
如果 T1
和 T2
位于同一层次结构中,并且它们最近的共同祖先 Parent
不是 Reference
、Struct
、Int
、Float
也不 是 Value
,则结果类型为 Parent+
。这被称为虚拟类型,它基本上意味着编译器现在将该类型视为 Parent
或其任何子类型。
例如
class Foo
end
class Bar < Foo
end
class Baz < Foo
end
bar = Bar.new
baz = Baz.new
# Here foo's type will be Bar | Baz,
# but because both Bar and Baz inherit from Foo,
# the resulting type is Foo+
foo = rand < 0.5 ? bar : baz
typeof(foo) # => Foo+
相同大小元组的联合¶
两个相同大小元组的联合将产生一个元组类型,该类型在每个位置都有类型的联合。
例如
t1 = {1, "hi"} # Tuple(Int32, String)
t2 = {true, nil} # Tuple(Bool, Nil)
t3 = rand < 0.5 ? t1 : t2
typeof(t3) # Tuple(Int32 | Bool, String | Nil)
具有相同键的命名元组的联合¶
两个具有相同键(无论其顺序如何)的命名元组的联合将产生一个命名元组类型,该类型在每个键中都有类型的联合。键的顺序将是左侧元组中的键的顺序。
例如
t1 = {x: 1, y: "hi"} # Tuple(x: Int32, y: String)
t2 = {y: true, x: nil} # Tuple(y: Bool, x: Nil)
t3 = rand < 0.5 ? t1 : t2
typeof(t3) # NamedTuple(x: Int32 | Nil, y: String | Bool)