Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Typecasting

First of all, what is typecasting? We know what a type is, but what is casting? Casting is the act of converting one type into another. Different types are incompatible with one another, even i32 and u32 are incompatible, even though both of them have the same bit width. For example, the i32 value of -233235653 would actually be 4061731643 when the bits would be interpreted as an u32 instead. And if the 32 bits of the i32 value would be interpreted as an f32 value, we would end up with the number -3.032584e30. (The bit pattern of this example is 1111 0010 0001 1001 0001 1011 0011 1011)

As you can see, just storing the bits of one number into the bits of another number is not enough. This is the reason to why we need to convert, or cast types. Try to compile this program and see what the compiler tells you:

def main():
    i32 val = 3.3;

The compiler will throw an error like:

Parse Error at main.ft:2:15
 -- Type mismatch of expression 3.3;
 -- Expected i32 but got f32

and it will tell you exactly what went wrong. The type of i32 was expected, beause we want to store something of type i32 in the variable val, but we provided a floating point number, which defaults to f32. So, we need to convert the type of f32 to i32 in this case.

Explicit Typecasting

Types can be explicitely cast like this:

def main():
    i32 val = i32(3.3);

We just write the type we want to have like i32 in this case followed by an open paren ( and then the expression which has a different type and then the closing paren ).

Also, every type can be cast to an str type! Try to run this program and see what happens:

use Core.print

def main():
    f32 fval = 3.3;
    str message = "fval = ";
    message += str(fval);
    message += "\n";
    print(message);

You will see the message

fval = 3.3

printed to the console.

Okay, and now a bit more complicated example:

use Core.print

def main():
    f32 fval = 3.7;
    i32 ival = i32(fval);
    str message = "fval = ";
    message += str(fval);
    message += ", ival = ";
    message += str(ival);
    message += "\n";
    print(message);

This small program prints this message to the console:

fval = 3.7, ival = 3

You maybe expected a result like

fval = 3.7, ival = 4

but here comes some very important information about type casting: When casting floating point types to integer types the fractional part is simply cut off. Its never rounded, only cut. But that definitely can be a good thing, if used correctly. Just remember, that when casting floating point values to integer values you will simply loose the fractional information.

Implicit Typecasting

Everything we have discussed up until now was regarding the act of explicitely casting values. But, some values can be cast implicitely by the compiler. There exists one rule of thumb in Flint: You can cast types implicitely if you won't loose any information through that cast. So, you can happily implicitely cast i32 to f32 but you cannot implicitely cast f32 to i32. Also, pretty much any type can be implicitely cast to a str type too! Here's how this looks in action:

use Core.print

def main():
    f32 fval = 3;
    str message = "fval = ";
    message += fval;
    message += "\n";
    print(message);

Notice how we did not write any explicit casting whatsoever? This program prints this message to the console:

fval = 3.0

Note that the floating point to string conversion happened implicitely?

This behaves differntly in the current release of Flint

Currently, you won't see fval = 3.0 printed to the console, but rather fval = 3. This is not a big deal, it only happens when the fractional part of the floating point value is zero, but this behaviour is a bit misleading, as you now could think that the value used to print 3 is an integer type, not a floating point type. Printing 3.0 makes this unambiguous and is considered the correct way to do it.