Types, part 1
In the "Syntax" section compute
's input parameters were of type u32
.
Let's unpack what that means.
Primitive types
u32
is one of Rust's primitive types. Primitive types are the most basic building blocks of a language.
They're 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. We'll 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 two's 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... what's the type of 42
?
The Rust compiler will try to infer the type of a literal based on how it's used.
If you don't 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 that's
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
is2
, not2.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 won't automatically convert a value from one type to
another3,
even if the conversion is lossless. You have to do it explicitly.
For example, you can't 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;
It'll throw a compilation error:
error[E0308]: mismatched types
|
3 | let a: u32 = b;
| --- ^ expected `u32`, found `u8`
| |
| expected due to this
|
We'll see how to convert between types later in this course.
Further reading
- The integer types section in the official Rust book
A bit is the smallest unit of data in a computer. It can only have two values: 0
or 1
.
Rust doesn't let you define custom operators, but it puts you in control of how the built-in operators behave. We'll talk about operator overloading later in the course, after we've covered traits.
There are some exceptions to this rule, mostly related to references, smart pointers and ergonomics. We'll cover those later on. A mental model of "all conversions are explicit" will serve you well in the meantime.
Exercise
The exercise for this section is located in 02_basic_calculator/01_integers