Type conversion
In the previous lesson, a type was the shape of a box. But sometimes a value sits in a box of one shape while you need it in another. Then you move it over, and in Swift you always do that explicitly, with a separate piece of code.
Here is a task from a shop: an item costs 2.5, and you take 3 of them. The count is a whole number, the price a fractional one. To multiply them, you first move the count into a box for fractional numbers:
let count = 3
let price = 2.5
let total = Double(count) * price
print(total) // 7.5Let's walk through the example line by line:
countgets the typeInt: the number3has no dot.pricegets the typeDouble.Double(count)takes3fromcountand makes the fractional3.0out of it. Both factors are nowDouble, and3.0 * 2.5gives7.5.print(total)prints7.5.
What happens if you remove Double(…)?
print(count * price) // ✗error: binary operator '*' cannot be applied to operands of type 'Int' and 'Double'The * sign can multiply an Int by an Int and a Double by a Double, but not an Int by a Double. Swift does not turn one into the other on its own, even when that looks safe.
Interviewers like to ask: "Does Swift convert number types implicitly?" The answer is no. Values of different number types do not mix in one operation until one of them is converted explicitly.
Explicit conversion
A type conversion is written as the name of the target type with the value in parentheses. The code Double(count) has two parts:
Double— the type the value is moved into;(count)— the value being moved.
The result is a new value of the target type. count itself does not change and stays an Int.
Three directions
From Int to Double
Double(…) makes a fractional number out of a whole one, and nothing is lost: 3 becomes 3.0. You pick this direction when a fractional number takes part in the calculation, like the price in the example above.
From Double to Int
A box for whole numbers has no room for a fractional part. Int(…) drops it rather than rounding. A negative number loses its fractional part the same way, so the result ends up closer to zero:
let height = 2.9
let temperature = -2.9
print(Int(height), Int(temperature)) // 2 -2Int(height)cuts the fractional part off2.9and gives2, not3.Int(temperature)cuts the fractional part off-2.9and gives-2, not-3.
Here the box picture stops working. Something that did not fit into a box stays next to it, whole. But the fractional part is gone for good after Int(…), and Swift warns you neither when compiling nor when running.
From a number to a string
String(…) turns a number into text: String(count) gives the string "3", and String(price) the string "2.5". A number and a string print the same, but to the program they are different things. You calculate with a number, while text can be attached to other text: "Count: " + String(count) gives Count: 3.
Numbers written in code
A number written right in the code gets its type from where it stands. In let count = 3 it becomes an Int. But in print(3 * 2.5) it stands next to a fractional number, so Swift makes 3 fractional right away, and the line prints 7.5. The constant count has no such freedom: its type Int was picked when it was declared.
Common mistake
The conversion was wrapped around the whole multiplication:
print(Double(count * price)) // ✗error: binary operator '*' cannot be applied to operands of type 'Int' and 'Double'The compiler first checks what is inside the parentheses: count * price. That is still an Int and a Double, and * cannot multiply them by each other. So the program does not compile, and the Double(…) outside does not help. Convert the value count itself: Double(count) * price.
Key points
- Swift does not convert types on its own: an
Intand aDoubledo not mix in one operation. - A conversion is written as the type's name with the value in parentheses:
Double(count), andcountitself stays anInt. Int(…)drops the fractional part rather than rounding:Int(2.9)is2, andInt(-2.9)is-2.String(…)turns a number into text that can be attached to other text.