Files
100-exercises-to-learn-rust/book/src/02_basic_calculator/10_as_casting.md
Luca Palmieri 96f06708b0 Render the book in PDF using pandoc and LaTeX. (#126)
* Render the book in PDF using `pandoc` and LaTeX.

* Fix installs.

* Go the apt-get route

* Another attempt

* Avoid installing twice.

* Re-order.

* Add more packages.

* Minimise deps. Fix link checker.

* Missing package.

* Missing package.

* Missing package.

* More packages.

* Missing package.

* Missing package.

* More packages...

* Remove.

* Fix link checker.

* Fix link checker.

* Fix path.

* Add subtitle.

* Avoid running over the right margin.

* Avoid running over the right margin.

* Formatting
2024-08-05 17:52:15 +02:00

2.9 KiB
Raw Blame History

Conversions, pt. 1

Weve repeated over and over again that Rust wont 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:

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:

// 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, lets 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. Thats… 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 well 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, youll have to rely on different conversion mechanisms (fallible and infallible), which well explore later on.

Further reading

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