Files
100-exercises-to-learn-rust/book/src/02_basic_calculator/01_integers.md
Luca Palmieri 1aae615bb4 Automatically add exercise links to sections. (#52)
We use an mdbook preprocessor to automatically generate links to the relevant exercise for each section.
We remove all existing manual links and refactor the deploy process to push the rendered book to a branch.
2024-05-24 18:15:38 +02:00

5.2 KiB
Raw Blame History

Types, part 1

In the “Syntax” section computes input parameters were of type u32.
Lets unpack what that means.

Primitive types

u32 is one of Rusts primitive types. Primitive types are the most basic building blocks of a language. Theyre built into the language itself—i.e. they are not defined in terms of other types.

You can combine these primitive types to create more complex types. Well see how soon enough.

Integers

u32, in particular, is an unsigned 32-bit integer.

An integer is a number that can be written without a fractional component. E.g. 1 is an integer, while 1.2 is not.

Signed vs. unsigned

An integer can be signed or unsigned.
An unsigned integer can only represent non-negative numbers (i.e. 0 or greater). A signed integer can represent both positive and negative numbers (e.g. -1, 12, etc.).

The u in u32 stands for unsigned.
The equivalent type for signed integer is i32, where the i stands for integer (i.e. any integer, positive or negative).

Bit width

The 32 in u32 refers to the number of bits1 used to represent the number in memory.
The more bits, the larger the range of numbers that can be represented.

Rust supports multiple bit widths for integers: 8, 16, 32, 64, 128.

With 32 bits, u32 can represent numbers from 0 to 2^32 - 1 (a.k.a. u32::MAX).
With the same number of bits, a signed integer (i32) can represent numbers from -2^31 to 2^31 - 1 (i.e. from i32::MIN to i32::MAX).
The maximum value for i32 is smaller than the maximum value for u32 because one bit is used to represent the sign of the number. Check out the twos complement representation for more details on how signed integers are represented in memory.

Summary

Combining the two variables (signed/unsigned and bit width), we get the following integer types:

Bit width Signed Unsigned
8-bit i8 u8
16-bit i16 u16
32-bit i32 u32
64-bit i64 u64
128-bit i128 u128

Literals

A literal is a notation for representing a fixed value in source code.
For example, 42 is a Rust literal for the number forty-two.

Type annotations for literals

But all values in Rust have a type, so… whats the type of 42?

The Rust compiler will try to infer the type of a literal based on how its used.
If you dont provide any context, the compiler will default to i32 for integer literals.
If you want to use a different type, you can add the desired integer type as a suffix—e.g. 2u64 is a 2 thats explicitly typed as a u64.

Underscores in literals

You can use underscores _ to improve the readability of large numbers.
For example, 1_000_000 is the same as 1000000.

Arithmetic operators

Rust supports the following arithmetic operators2 for integers:

  • + for addition
  • - for subtraction
  • * for multiplication
  • / for division
  • % for remainder

Precedence and associativity rules for these operators are the same as in mathematics.
You can use parentheses to override the default precedence. E.g. 2 * (3 + 4).

⚠️ Warning

The division operator / performs integer division when used with integer types. I.e. the result is truncated towards zero. For example, 5 / 2 is 2, not 2.5.

No automatic type coercion

As we discussed in the previous exercise, Rust is a statically typed language.
In particular, Rust is quite strict about type coercion. It wont automatically convert a value from one type to another3, even if the conversion is lossless. You have to do it explicitly.

For example, you cant assign a u8 value to a variable with type u32, even though all u8 values are valid u32 values:

let b: u8 = 100;
let a: u32 = b;

Itll throw a compilation error:

error[E0308]: mismatched types
  |
3 |     let a: u32 = b;
  |            ---   ^ expected `u32`, found `u8`
  |            |
  |            expected due to this
  |

Well see how to convert between types later in this course.

Further reading


  1. A bit is the smallest unit of data in a computer. It can only have two values: 0 or 1.↩︎

  2. Rust doesnt let you define custom operators, but it puts you in control of how the built-in operators behave. Well talk about operator overloading later in the course, after weve covered traits.↩︎

  3. There are some exceptions to this rule, mostly related to references, smart pointers and ergonomics. Well cover those later on. A mental model of “all conversions are explicit” will serve you well in the meantime.↩︎