4.3isize, usize, and integer literals
Two integer types didn't fit last lesson's tidy table, because their size isn't in their name: it's in your computer.
The pointer-sized pair
usize (unsigned) and isize (signed) match the width of a memory address on the machine the program is compiled for: 8 bytes on the 64-bit hardware you almost certainly own, 4 on older 32-bit targets. Proof by asking:
fn main() {
println!("usize here: {} bytes", size_of::<usize>());
}usize here: 8 bytes
Why would a language with ten perfectly good fixed-size integers add two stretchy ones? Because some numbers are about memory itself: how many elements a collection holds, which position you're reading. A memory-sized number deserves a memory-sized type, whatever the machine. That's usize's entire career, and you've met one already: size_of returns a usize.
You won't choose usize often; it arrives on its own. When chapter 18's collections appear, positions and lengths will be usize by decree, and the no-implicit-conversions rule from lesson 4.2 will occasionally make you convert into it deliberately. For ordinary arithmetic, the advice stands: i32 by default, usize when you're talking about memory, isize nearly never (it exists for some rare pointer math; file it under trivia for now).
Integer literals, the complete kit
You know integer literals default to i32 and tolerate underscores (1_000_000, lesson 1.10). Here's the rest of the syntax, all of it occasionally useful and all of it quiz bait.
A literal can carry its type as a suffix, fused right onto the number:
let small = 42u8; // same as: let small: u8 = 42;
let big = 42u64; // same as: let big: u64 = 42;
Suffix and annotation mean the same thing; the suffix is handy when there's no let to annotate, like an argument in a function call. (With neither, you get i32.)
Literals can also be written in other bases: prefix 0x for hexadecimal, 0o for octal, 0b for binary. The value is identical regardless of spelling; only the notation changes, and you pick whichever matches how you're thinking about the number:
fn main() {
println!("{}", 255);
println!("{}", 0xFF);
println!("{}", 0o377);
println!("{}", 0b1111_1111);
}255
255
255
255
Four spellings, one value. Binary spelling plus underscores is the classic combination for bit-pattern work (chapter 6's optional bitwise lesson), and hex is the native tongue of anything byte-flavored. There's even a character form, b'A', meaning "the u8 whose value is this character's code" (65 here); it'll make more sense after lesson 4.8.
One more piece of compile-time safety rounds out the kit. A literal that doesn't fit its type is rejected outright:
fn main() {
let x: u8 = 300;
println!("{x}");
}error: literal out of range for `u8`
--> src/main.rs:2:17
|
2 | let x: u8 = 300;
| ^^^
|
= note: the literal `300` does not fit into the type `u8` whose range is `0..=255`
= note: `#[deny(overflowing_literals)]` on by default
The compiler quotes you the type's range in the rejection. What happens when a value strays out of range at runtime, where no compiler can pre-check it, is the next lesson, and it's a better story than most languages can tell.
Quiz time
Question #1
What value does each print? 0b1010, 0x10, 1_0_0
Show solution
10 (binary: 8+2), 16 (hex: one sixteen, zero ones), and 100 (underscores are ignored wherever they fall, even in silly places).
Question #2
What's the type of each variable?
let a = 7;
let b = 7u16;
let c: i64 = 7;
let d = size_of::<bool>();Show solution
a is i32 (the default), b is u16 (suffix), c is i64 (annotation), and d is usize (whatever size_of returns, that's its type; no conversion happened because none was needed).
Question #3
A teammate writes let flags: u8 = 0b1_0000_0000; and insists binary literals are always fine. Predict the compiler's verdict.
Show solution
Rejected: that's binary for 256, one past u8's ceiling, and the literal-out-of-range error will say so, range quoted. The base never matters; the value always does.
Next: what happens when arithmetic itself, not a literal, produces a number too big for its type. Rust's answer involves a panic, a wrap, and a menu.