Conversions, pt. 1

We've repeated over and over again that Rust won't perform implicit type conversions for integers.
How do you perform explicit conversions then?

as

You can use the as operator to convert between integer types.
as conversions are infallible.

For example:

#![allow(unused)]
fn main() {
let a: u32 = 10;

// Cast `a` into the `u64` type
let b = a as u64;

// You can use `_` as the target type
// if it can be correctly inferred 
// by the compiler. For example:
let c: u64 = a as _;
}

The semantics of this conversion are what you expect: all u32 values are valid u64 values.

Truncation

Things get more interesting if we go in the opposite direction:

#![allow(unused)]
fn main() {
// A number that's too big 
// to fit into a `u8`
let a: u16 = 255 + 1;
let b = a as u8;
}

This program will run without issues, because as conversions are infallible. But what is the value of b? When going from a larger integer type to a smaller, the Rust compiler will perform a truncation.

To understand what happens, let's start by looking at how 256u16 is represented in memory, as a sequence of bits:

 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
|               |               |
+---------------+---------------+
  First 8 bits    Last 8 bits

When converting to a u8, the Rust compiler will keep the last 8 bits of a u16 memory representation:

 0 0 0 0 0 0 0 0 
|               |
+---------------+
  Last 8 bits

Hence 256 as u8 is equal to 0. That's... not ideal, in most scenarios.
In fact, the Rust compiler will actively try to stop you if it sees you trying to cast a literal value which will result in a truncation:

error: literal out of range for `i8`
  |
4 |     let a = 255 as i8;
  |             ^^^
  |
  = note: the literal `255` does not fit into the type `i8` 
          whose range is `-128..=127`
  = help: consider using the type `u8` instead
  = note: `#[deny(overflowing_literals)]` on by default

Recommendation

As a rule of thumb, be quite careful with as casting.
Use it exclusively for going from a smaller type to a larger type. To convert from a larger to smaller integer type, rely on the fallible conversion machinery that we'll explore later in the course.

Limitations

Surprising behaviour is not the only downside of as casting. It is also fairly limited: you can only rely on as casting for primitive types and a few other special cases.
When working with composite types, you'll have to rely on different conversion mechanisms (fallible and infallible), which we'll explore later on.

Further reading

  • Check out Rust's official reference to learn the precise behaviour of as casting for each source/target combination, as well as the exhaustive list of allowed conversions.

Exercise

The exercise for this section is located in 02_basic_calculator/10_as_casting