---
title: "4.3 isize, usize, and integer literals"
description: "Rust's pointer-sized integers, where usize shows up, and the full integer literal syntax: suffixes, underscores, hex, octal, and binary."
url: "https://learnrust.net/chapter-4/isize-usize-and-integer-literals/"
last_updated: "2026-06-11"
---

# 4.3 isize, 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:

```rust
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](https://learnrust.net/chapter-4/integer-types/) 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](https://learnrust.net/chapter-1/literals-and-operators/)). 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:

```rust
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:

```rust
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](https://learnrust.net/chapter-4/char-and-unicode/).

One more piece of compile-time safety rounds out the kit. A literal that doesn't fit its type is rejected outright:

```rust
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`

<details class="solution">
<summary>Show solution</summary>

`10` (binary: 8+2), `16` (hex: one sixteen, zero ones), and `100` (underscores are ignored wherever they fall, even in silly places).

</details>

**Question #2**

What's the type of each variable? 

```rust
let a = 7;
let b = 7u16;
let c: i64 = 7;
let d = size_of::<bool>();
```

<details class="solution">
<summary>Show solution</summary>

`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).

</details>

**Question #3**

A teammate writes `let flags: u8 = 0b1_0000_0000;` and insists binary literals are always fine. Predict the compiler's verdict.

<details class="solution">
<summary>Show solution</summary>

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.

</details>

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.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
