聯合類型¶
變數或表達式的類型可以由多種類型組成。這稱為聯合類型。例如,當在不同的 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)