Step 1 of 7
Integer types and literals
In the last module, whole numbers were i32. Rust actually has a family of integer types. They differ in two ways: how many bits of memory they use (more bits, bigger range), and whether they're signed (can be negative, names start with i) or unsigned (zero or positive only, names start with u).
| Bits | Signed | Range | Unsigned | Range |
|---|---|---|---|---|
| 8 | i8 |
-128 to 127 | u8 |
0 to 255 |
| 16 | i16 |
-32,768 to 32,767 | u16 |
0 to 65,535 |
| 32 | i32 |
about -2.1 to 2.1 billion | u32 |
0 to about 4.3 billion |
| 64 | i64 |
about ±9.2 × 10¹⁸ | u64 |
0 to about 1.8 × 10¹⁹ |
| 128 | i128 |
about ±1.7 × 10³⁸ | u128 |
0 to about 3.4 × 10³⁸ |
| machine | isize |
same as i64 on 64-bit computers |
usize |
same as u64 on 64-bit computers |
Every integer type knows its own limits, as the constants MIN and MAX:
fn main() {
println!("u8: {} to {}", u8::MIN, u8::MAX);
println!("i8: {} to {}", i8::MIN, i8::MAX);
println!("i32: {} to {}", i32::MIN, i32::MAX);
println!("u64 max: {}", u64::MAX);
}
u8: 0 to 255
i8: -128 to 127
i32: -2147483648 to 2147483647
u64 max: 18446744073709551615
u8::MAX means "the item called MAX that belongs to u8". The :: is the path separator; you'll use it a lot.
Which one should you use?
i32is the default and a good general choice.- Use an unsigned type when a negative value would be a bug: a quantity, a count, an age. The type then documents the rule and the compiler enforces it.
u8for bytes (0 to 255, like the red, green and blue parts of a color).i64/u64when values can pass about 2 billion: money in cents, file sizes, milliseconds.usizefor lengths and positions of things in memory. That's what.len()returns.
Writing integer literals
A number typed into code is a literal, and Rust has several ways to write one:
fn main() {
let population = 8_100_000_000_u64;
let red = 0xff;
let flags = 0b1010;
let permissions = 0o755;
let small = 200u8;
println!("{population} {red} {flags} {permissions} {small}");
}
8100000000 255 10 493 200
_separates digits so they're easy to read; the compiler ignores it.1_000_000is one million.0xstarts a hexadecimal number (base 16, digits 0-9 and a-f):0xffis 255. Common for colors and bytes.0bstarts a binary number (base 2):0b1010is 10.0ostarts an octal number (base 8), used for Unix file permissions.- A suffix sets the type:
200u8is au8,8_100_000_000_u64au64. Without the suffix, the population would be ani32, which can't hold 8.1 billion.
Common mistakes
A literal that doesn't fit its type is an error:
fn main() {
let level: u8 = 256;
println!("{level}");
}
error: literal out of range for `u8`
--> src/main.rs:2:21
|
2 | let level: u8 = 256;
| ^^^
- A negative value in an unsigned type:
let stock: u32 = -1;giveserror[E0600]: cannot apply unary operator `-` to type `u32`. - Mixing integer types: if
ais au32andbani32,a + biserror[E0308]: mismatched types. Rust never converts between number types silently; later in this module you'll learn to convert explicitly.
Your task
Fill in the blanks so the program prints the range of i16 and the largest u16:
i16: -32768 to 32767
u16 max: 65535
Challenge 2
Fill in the literals so the program prints 255 10 1000000. Write 255 in hexadecimal, 10 in binary, and one million with _ separators.
Challenge 3
A game stores three numbers. Declare each with the type that fits:
lives:3, never negative and never above 5, sou8gold:5_000_000_000(write it with underscores), never negative and too big for 32 bits, sou64temperature:-12, can be negative, soi16
Then print lives=3 gold=5000000000 temp=-12.