Type autocasting

Crystal transparently casts elements of certain types when there is no ambiguity.

Number autocasting

Values of a numeric type autocast to a larger one if no precision is lost:

  1. def foo(x : Int32) : Int32
  2. x
  3. end
  4. def bar(x : Float32) : Float32
  5. x
  6. end
  7. def bar64(x : Float64) : Float64
  8. x
  9. end
  10. foo 0xFFFF_u16 # OK, an UInt16 always fit an Int32
  11. foo 0xFFFF_u64 # OK, this particular Int64 fit in an Int32
  12. bar(foo 1) # Fails, casting an Int32 to a Float32 might lose precision
  13. bar64(bar 1) # OK, a Float32 can be autocasted to a Float64

Number literals are always casted when the actual value of the literal fits the target type, despite of its type.

Expressions are casted (like in the last example above), unless the flag no_number_autocast is passed to the compiler (see Compiler features).

If there is ambiguity, for instance, because there is more than one option, the compiler throws an error:

  1. def foo(x : Int64)
  2. x
  3. end
  4. def foo(x : Int128)
  5. x
  6. end
  7. bar = 1_i32
  8. foo bar # Error: ambiguous call, implicit cast of Int32 matches all of Int64, Int128

Autocasting at the moment works only in two scenarios: at function calls, as shown so far, and at class/instance variable initialization. The following example shows an example of two situations for an instance variable: casting at initialization works, but casting at an assignment doesn’t:

  1. class Foo
  2. @x : Int64 = 10 # OK, 10 fits in an Int64
  3. def set_x(y)
  4. @x = y
  5. end
  6. end
  7. Foo.new.set_x 1 # Error: "at line 5: instance variable '@x' of Foo must be Int64, not Int32"

Symbol autocasting

Symbols are autocasted as enum members, therefore enabling to write them more succinctly:

  1. enum TwoValues
  2. A
  3. B
  4. end
  5. def foo(v : TwoValues)
  6. case v
  7. in TwoValues::A
  8. p "A"
  9. in TwoValues::B
  10. p "B"
  11. end
  12. end
  13. foo :a # autocasted to TwoValues::A