Tracks
/
Crystal
Crystal
/
Temario
/
Tipo unión
Ti

Tipo unión en Crystal

1 ejercicio

Acerca de Tipo unión

Crystal permite que una variable conste de varios tipos. A esto se le llama tipo unión. En Crystal, es habitual que el compilador infiera un tipo unión.

Note

Un tipo unión sigue siendo un único tipo en tiempo de ejecución, incluso si consta de varios tipos. Esto significa que, si un tipo unión está formado por String e Int32, no será ambos a la vez. En su lugar, será o bien un String o bien un Int32.

Un tipo unión se declara separando los tipos con una barra vertical (|). A menudo se colocan entre paréntesis, pero no es obligatorio. El tipo unión más común es (T | Nil), donde T es un tipo, que puede darse en métodos que pueden devolver Nil. Esto también se conoce como que una variable es nilable.

foo : String = "Hello"
foo = nil # Error: type must be String, not (String | Nil)

foo : (String | Nil) = "Hello"
foo = nil

No se limita a solo dos tipos, sino que puede tener tantos como quieras.

foo : (String | Int32 | Nil | Float64) = "Hello"
foo = 1
foo = nil
foo = 1.0

typeof vs Object#class

Hay dos formas de obtener el tipo de una variable. O bien usando typeof, o bien usando Object#class. La diferencia es que typeof devuelve el tipo de una variable en tiempo de compilación, mientras que Object#class devuelve el tipo en tiempo de ejecución. Esto significa que, si quieres ver si una variable es un tipo unión, por ejemplo, Object#class no podrá decírtelo, ya que solo devuelve el tipo en tiempo de ejecución, que es un único tipo.

foo = 0 == 0 ? "a" : 1
typeof(foo) # => (String | Int32)
foo.class # => String

Operaciones con tipos unión

Como un tipo unión es un único tipo en tiempo de ejecución, todas las operaciones estándar funcionan con él. Pero al compilar el código, el compilador necesitará saber de qué tipo se trata. Por lo tanto, el código debe estar configurado de tal forma que solo pueda ser uno de los tipos cuando se quieran usar las operaciones específicas de cada tipo.

foo : (String | Int32) = "Hello"
foo.downcase # Error: undefined method 'downcase' for (String | Int32)

Crystal sí que tiene un método particular para los tipos unión: el método is_a?, que toma un tipo como argumento y devuelve un Boolean. El método nil? es un atajo para is_a?(Nil). Poner el método is_a? en una expresión de control le dirá al compilador de qué tipo se trata y, con ello, garantizará que es ese tipo. Y en una rama else se garantizará que no es ese tipo.

foo : (String | Int32) = "Hello"
if foo.is_a?(String)
  typeof(foo)  # => String
  foo.downcase # => "hello"
end

Este is_a? no se limita a tener un único tipo como argumento, sino que también puede recibir un tipo unión. Además, se puede combinar con && para varios tipos.

Note

El método is_a?, cuando se usa junto con una expresión de control, no puede aplicarse a una variable de instancia ni a una variable de clase. En su lugar, hay que asignarlas primero a una variable local.

as

Una forma de convertir un tipo unión en un único tipo es hacer que una rama solo se pueda ejecutar si el tipo es un tipo concreto. Otro enfoque es usar el método as. Este convierte un tipo unión en un único tipo mediante una comprobación en tiempo de ejecución. Se lanzará una excepción si el tipo no es el esperado.

foo : String | Int32 = "Hello"
foo.as(String).downcase # => "hello"

foo.as(Int32) # Error: can't cast String to Int32
Caution

Este enfoque solo está pensado para cuando estás seguro de que el tipo es el esperado o para cuando quieres lanzar una excepción si no lo es.

Usar este enfoque con una configuración inadecuada puede dar lugar a un comportamiento inesperado.

as?

as? funciona de forma muy similar a as, pero devolverá nil en lugar de lanzar una excepción si el tipo no es el esperado. Esto significa que devolverá un tipo unión del tipo esperado y Nil.

foo : (String | Int32) = "Hello"
foo.as?(String).downcase # => "hello"

foo.as?(Int32) # => nil

Forma abreviada para nilable

Que algo sea nilable significa que una variable puede ser o bien un tipo, o bien Nil. Esto se puede escribir como (T | Nil). Pero como los tipos nilables son relativamente comunes, existe una forma abreviada: T?.

# This:
foo : (String | Nil) = "Hello"
foo = nil

# Is the same as:
foo : String? = "Hello"
foo = nil

Inferencia de tipos unión

El compilador inferirá un tipo unión si no está seguro de qué tipo se trata. Por ejemplo, en el siguiente código el compilador no sabrá de qué tipo es foo, ya que puede ser o bien un String, o bien un Int32.

if true
  foo = 1
else
  foo = "Hello"
end

typeof(foo) # => (Int32 | String)

Esta inferencia ocurre automáticamente, y hay otros escenarios en los que, por ejemplo, el compilador inferirá que un método devuelve un tipo unión.

character = "Hello world"[10]?
typeof(character) # => (Char | Nil)
Editar en GitHub El enlace se abre en una nueva ventana o pestaña

Aprende Tipo unión

La práctica está bloqueada

Desbloquea 1 ejercicio más para practicar Tipo unión