Spec-Zone.ru › Crystal

Объединённые типы

Тип переменной или выражения может состоять из нескольких типов. Это называется объединённым типом. Например, при присваивании одной и той же переменной в разных ветвях 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)

To the extent possible under law, the persons who contributed to this workhave waived
all copyright and related or neighboring rights to this workby associating CC0 with it.
https://crystal-lang.org/reference/1.2/syntax_and_semantics/union_types.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API