</>
Skip to content
Rust lessons (8/43)

Rust — Data Types

Two families

Rust types split into scalars (single values) and compounds (groups — tuples, arrays; their own lesson). Every value has a known-at-compile-time type.

Integers — signed and unsigned

LengthSignedUnsignedRange (signed)
8-biti8u8−128 → 127
16-biti16u16±32k
32-biti32u32±2.1 billion
64-biti64u64huge
arch-sizeisizeusizematches platform pointer
let a: i32 = -42;      // default integer type
let b: u8 = 255;       // max for u8
let c = 10_000_000;    // separators allowed

i32 is the default when inference can't decide. Choose unsigned (u*) when negatives are impossible — ages, counts, indices. usize is the type of indexes/collections' .len().

Floats

let x = 2.5;        // f64 by default — double precision
let y: f32 = 2.5;   // single precision

Same IEEE-754 quirks as other languages:

0.1 + 0.2 == 0.3          // false!
(0.1 + 0.2).to_string()   // "0.30000000000000004"

Booleans and char

let active: bool = true;
let letter: char = 'z';       // SINGLE quotes in Rust!
let emoji = '😀';              // full Unicode ✓

Note carefully: 'a' is a char, "a" is a string slice. Double quotes never make chars.

Numeric operations

let sum = 5 + 10;         // i32 add
let q = 17 / 5;           // 3   ← INTEGER division truncates!
let r = 17 % 5;           // 2
let f = 17.0 / 5.0;       // 3.4 — float division needs float operands
let p = 2i32.pow(8);      // 256

Mixed-type math is an error:

let a: i32 = 5;
let b: f64 = 2.0;
// a + b            ❌ no implicit coercion in Rust!
let c = a as f64 + b;  // ✓ explicit `as` cast

Rust refuses silent conversions — precision-loss bugs become compile errors instead.

Where inference stops

let parsed = "42".parse();     // ❌ ambiguous — could be any numeric type
let parsed: i32 = "42".parse()? or .unwrap();   // ✓ annotate to resolve
let parsed = "42".parse::<i32>().unwrap();      // turbofish alternative

When multiple types fit, you must disambiguate — annotation or the ::<> turbofish.

Integer overflow — debug vs release

let x: u8 = 255;
let y = x + 1;
  • Debug builds: panic! ("attempt to add with overflow") — loud, immediate
  • Release builds: wraps around silently to 0

Never rely on wrapping; use explicit methods when wrap-around is intended:

let y = x.wrapping_add(1);     // documented wrap
let z = x.checked_add(1);      // returns Option — None on overflow
let w = x.saturating_add(1);   // clamps at maximum

Type conversion with as

let big: i64 = 100;
let small = big as i32;      // explicit, may truncate
let letter_code = 'A' as u8; // 65

as performs raw conversions including lossy ones — use deliberately, comment when truncating.

Mini Practice

  1. Print the max values of i8/u8/i16 using their MAX constants.
  2. Show 17 / 5 vs 17.0 / 5.0.
  3. Fix "42".parse() ambiguity two ways (annotation + turbofish).
  4. Trigger debug overflow panic with u8; rewrite with checked_add.
  5. Cast 'Z' to u8 and back to char.

Next: constants → (already covered inside variables — next new topic: operators)

Related Topics

Frequently Asked Questions about Data Types

What is Data Types in Rust?

Data Types is a fundamental concept in Rust. This lesson explains it step by step with clear examples, making it easy for beginners to understand.

How do I learn Data Types?

Start by reading the explanation above, then try the code examples. Practice by modifying the examples and experimenting with different values. Hands-on practice is the best way to learn Data Types.

Why is Data Types important in Rust?

Data Types is essential for Rust development. Understanding this concept will help you write better code and solve real-world problems more effectively.