Converting Data Between Types

Most of the time, you don’t have to declare types in Crystal, which can be a relief. Sometimes, though, you’ll need to make certain that you’re working with values of a certain type. You shouldn’t count much on Ruby-style automatic conversions: Crystal isn’t only strongly typed—it wants you to be very careful with conversions. Type conversion is something you’ll need to do explicitly in order to satisfy the Crystal compiler. (Though Crystal will deal with the simplest conversions, such as int8 to int32 automatically.)

Money provides a good example: currency rates are numbers with many digits after the decimal point, as in 1 USD is worth 64.34603985 Indian Rupees (INR) today. Although banks don’t use floating-point numbers for reasons of precision, they’ll work well for an example. Everything we type on a screen arrives in our program as a string, though. Adding numbers is quite different from doing the same with strings, and multiplying strings would be even stranger.

Consider the following, where you try to convert a string representing a currency rate into an integer with the to_i method:

 "64.34603985"​.​to_i​ ​# Runtime error: Invalid Int32: 64.34603985 (ArgumentError)

Ruby would happily return the value 64, but Crystal chokes on it at runtime. Crystal is more particular about the conversions it will do because different types have different to_ methods available. You can, for example, convert a string to a float:

 "64.34603985"​.​to_f​ ​# => 64.34603985

You can also convert a float into an integer with to_i, but it’s going to get truncated without warning:

 rate = 64.34603985
 rate.​to_i​ ​# => 64

How would you solve this compiler error?

 rate1 = 64.34603985
 rate2 = ​"7"
 rate1 + rate2 ​# Error: no overload matches 'Float64#+' with type String

If you want to add here, you should use to_i on rate2. to_s converts everything into a String, resulting in rate1 and rate2 being concatenated to each other. It makes the compiler happy but probably isn’t what you want:

 rate1 + rate2.​to_i​ ​# => 71.34603985
 rate1.​to_s​ + rate2 ​# => "64.346039857"

If, instead, you need to make sure you’re working with floating point numbers, you’ll use to_f:

 rate1.​to_f​ ​# => 64.34603985
 rate2.​to_f​ ​# => 7.0

Of course, there are times when conversions can’t work because the data doesn’t fit:

 curr = ​"Crystal"
 curr.​to_i​ ​# => Runtime error: Invalid Int32: Crystal (ArgumentError)

Crystal also offers more approaches for telling the compiler how you want it to handle types. When the compiler thinks a piece of data has a union type (A | B), but you’re sure that it’s of a particular type B, you can use as(B) to force the compiler to treat it as type B. Although it sure looks like it, this isn’t a conversion like the to_ methods above. Later in this chapter, in Controlling the Flow and Types, you’ll see how to test on a type of a variable, executing different code depending on the outcome.

Your Turn 1

Type conversion among integer types: Crystal is extremely precise about types, but so far you’ve only used to_i to convert numbers to integers. Crystal offers many different types of integers, though, from 8-bit to 64-bit, both signed and unsigned. How do you convert among those types?

It turns out that you don’t have to make the conversions explicit, but there are some tricky bits involving order. The Crystal compiler lets you mix integer types, but the result will default to the type of the first value used, not the largest value used. What do you think will be the results of the following:

 p int8 = 1_i8 ​# 8-bit signed integer
 p int16 = 16_i16 ​# 16-bit signed integer
 p int32 = 132_i32 ​# 32-bit signed integer
 p int64 = 164_i64 ​# 64-bit signed integer
 p uns64 = 264_u64 ​# 64-bit unsigned integer
 
 p int64 + int32 + uns64
 
 p int8 + int64
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset