Scalar & Compound Types

Rust · Lesson 5 of 31 7 min read Updated

Every value in Rust has a type, and the compiler must know every type before the program runs. Types decide how much memory a value takes, which operations are allowed and what happens at the edges, for example when a number becomes too big.

Rust has two families of basic types. Scalar types hold a single value: integers, floating-point numbers, booleans and characters. Compound types group several values: tuples and arrays. In this lesson you will meet all of them and learn how to print them.

Integers

An integer is a whole number. Rust has signed integers (can be negative, names start with i) and unsigned integers (zero or positive, names start with u). The number in the name is the size in bits.

SizeSignedUnsignedUnsigned range
8-biti8u80 to 255
16-biti16u160 to 65,535
32-biti32u320 to about 4.29 billion
64-biti64u640 to about 18 quintillion
128-biti128u128huge
pointer-sizedisizeusizedepends on the computer (64-bit on most laptops)

If you do not choose a type, Rust uses i32. usize is the type of indexes and lengths, so .len() returns a usize. Number literals can contain underscores for readability (1_00_000 and 100_000 are the same number), a type suffix (250u8) or a base prefix: 0xff (hex), 0o17 (octal), 0b1010 (binary). b'A' is the byte value of a letter, a u8.

Integer overflow

What happens when a u8 holding 250 gets 10 more? In a debug build, Rust stops the program with a panic ("attempt to add with overflow"). In a release build the value silently wraps around. Neither is what you want in real code, so Rust gives you methods that say exactly what you mean:

Rust
fn main() {
    let stock: u8 = 250;
    println!("{:?}", stock.checked_add(10));
    println!("{:?}", stock.checked_add(5));
    println!("{}", stock.wrapping_add(10));
    println!("{}", stock.saturating_add(10));
    println!("Largest i32: {}", i32::MAX);
}
Output
None
Some(255)
4
255
Largest i32: 2147483647

checked_add returns None when the answer does not fit (you will meet Some and None in Option & Result). wrapping_add wraps around: 260 minus 256 is 4. saturating_add stops at the maximum, 255.

Floating-point numbers

Floating-point numbers have a decimal point. Rust has f32 and f64; the default is f64, which is about as fast on modern processors and much more precise.

Rust
fn main() {
    let total_marks = 437;
    let subjects = 5;

    println!("Integer division: {}", total_marks / subjects);
    println!("Remainder: {}", total_marks % subjects);

    let average = total_marks as f64 / subjects as f64;
    println!("Average: {average}");

    let sum = 0.1 + 0.2;
    println!("0.1 + 0.2 = {sum}");
    println!("Rounded: {sum:.2}");
}
Output
Integer division: 87
Remainder: 2
Average: 87.4
0.1 + 0.2 = 0.30000000000000004
Rounded: 0.30

Three lessons hide in this example:

  • Dividing two integers gives an integer; the fraction is thrown away.
  • Rust never mixes types automatically. total_marks / 2.5 is a compile error because i32 and f64 are different types. Convert explicitly with as.
  • Floating-point numbers cannot store most decimal fractions exactly, so 0.1 + 0.2 is not exactly 0.3. Format with a fixed number of places for display, and never store money as f64. Store paise as an integer instead (129_900 paise is Rs. 1,299).

as also converts between integer types, but it can lose information: 3.99_f64 as i32 is 3 (the fraction is cut off), and 300_i32 as u8 is 44 (only the lowest 8 bits are kept). When a conversion might fail, prefer u8::try_from(value), which returns an error instead of a wrong number.

Booleans and characters

A bool is either true or false and is usually the result of a comparison. A char is one Unicode character, written in single quotes. Every char takes 4 bytes, so it can hold any letter from any language, plus symbols and emoji.

Rust
fn main() {
    let is_member = true;
    let has_coupon = 450 > 500;
    let rupee = '₹';
    let hindi_letter = 'अ';

    println!("Member: {is_member}, coupon: {has_coupon}");
    println!("Symbols: {rupee} {hindi_letter}");
    println!("Is 'a' alphabetic? {}", 'a'.is_alphabetic());
}
Output
Member: true, coupon: false
Symbols: ₹ अ
Is 'a' alphabetic? true

Text in double quotes, such as "Pune", is not a char. It is a string slice (&str), which you will study in Strings & UTF-8.

Tuples

A tuple groups a fixed number of values that can have different types. You read the parts by position (.0, .1, ...) or unpack them all at once with destructuring:

Rust
fn main() {
    let student = ("Isha", 19, 8.7);
    let (name, age, cgpa) = student;
    println!("{name} is {age} and has a CGPA of {cgpa}");
    println!("Age again: {}", student.1);
    println!("{student:?}");

    let point = (12, -4);
    println!("{point:#?}");
}
Output
Isha is 19 and has a CGPA of 8.7
Age again: 19
("Isha", 19, 8.7)
(
    12,
    -4,
)

Tuples and arrays cannot be printed with plain {}, because there is no single "user-friendly" way to show them. {:?} uses the Debug format, meant for programmers: strings appear in quotes. {:#?} is the "pretty" Debug format, which puts each part on its own line.

The empty tuple () is called the unit type. It means "no meaningful value" and is what a function returns when it returns nothing.

Arrays

An array holds a fixed number of values of the same type, stored next to each other in memory. The type is written [type; length]:

Rust
fn main() {
    let temps: [i32; 7] = [31, 33, 34, 29, 30, 35, 32];
    let empty_seats = [0; 4];

    println!("Monday: {}", temps[0]);
    println!("Days recorded: {}", temps.len());
    println!("{temps:?}");
    println!("{empty_seats:?}");
}
Output
Monday: 31
Days recorded: 7
[31, 33, 34, 29, 30, 35, 32]
[0, 0, 0, 0]

[0; 4] makes an array of four zeros. Indexes start at 0. If an index is outside the array, Rust does not read random memory as C would: when the compiler can see the bad index it refuses to compile, and otherwise the program panics with "index out of bounds". An array's length can never change; for a list that grows, use a Vec, covered in Vectors (Vec).

Common mistakes

  • Mixing integer and float types. 5 / 2.0 does not compile. Convert one side with as f64.
  • Expecting 7 / 2 to be 3.5. Integer division drops the fraction; it gives 3.
  • Storing money in f64. Rounding errors add up. Keep amounts in paise as integers.
  • Printing a tuple or array with {}. Use {:?} or {:#?}.
  • Using as for risky conversions. It silently cuts values; try_from reports the problem.

Key points

  • Integers come in signed (i8 to i128, isize) and unsigned (u8 to u128, usize) types; the default is i32.
  • Overflow panics in debug builds and wraps in release builds; use checked_, wrapping_ or saturating_ methods to be explicit.
  • f64 is the default float; floats are approximate, so do not use them for money.
  • bool is true/false, and char is a 4-byte Unicode character in single quotes.
  • Tuples group different types; arrays hold a fixed number of one type. Print both with {:?} or {:#?}.

Exercise

A shop in Jaipur sold 14, 9, 22 and 17 kurtas over four days. Store the numbers in an array of u32, compute the total and the average as an f64 with two decimal places, and store the best day as a tuple (day_number, sales). Print everything, including the tuple with {:?}.

Show solution
Rust
fn main() {
    let sales: [u32; 4] = [14, 9, 22, 17];
    let total = sales[0] + sales[1] + sales[2] + sales[3];
    let average = total as f64 / sales.len() as f64;
    let best_day = (3, sales[2]);

    println!("Total sold: {total}");
    println!("Average per day: {average:.2}");
    println!("Best day: {best_day:?}");
}
Output
Total sold: 62
Average per day: 15.50
Best day: (3, 22)

The total is a u32, so both it and sales.len() (a usize) are converted to f64 before dividing. You will learn to find the best day with a loop in Control Flow: if, loop, while & for.

Quick Quiz

1. What is the default integer type in Rust?

2. What does 200_u8.saturating_add(100) return?

3. What is the value of 7 / 2 when both are integers?

4. How do you print a tuple (1, "a") so that it shows (1, "a")?

Learn Rust with a mentor

Free lessons get you started. Our Programming & Software Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call