Scalar & Compound Types
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.
| Size | Signed | Unsigned | Unsigned range |
|---|---|---|---|
| 8-bit | i8 | u8 | 0 to 255 |
| 16-bit | i16 | u16 | 0 to 65,535 |
| 32-bit | i32 | u32 | 0 to about 4.29 billion |
| 64-bit | i64 | u64 | 0 to about 18 quintillion |
| 128-bit | i128 | u128 | huge |
| pointer-sized | isize | usize | depends 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:
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);
}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.
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}");
}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.5is a compile error becausei32andf64are different types. Convert explicitly withas. - Floating-point numbers cannot store most decimal fractions exactly, so
0.1 + 0.2is not exactly0.3. Format with a fixed number of places for display, and never store money asf64. Store paise as an integer instead (129_900paise 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.
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());
}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:
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:#?}");
}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]:
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:?}");
}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.0does not compile. Convert one side withas f64. - Expecting
7 / 2to 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
asfor risky conversions. It silently cuts values;try_fromreports the problem.
Key points
- Integers come in signed (
i8toi128,isize) and unsigned (u8tou128,usize) types; the default isi32. - Overflow panics in debug builds and wraps in release builds; use
checked_,wrapping_orsaturating_methods to be explicit. f64is the default float; floats are approximate, so do not use them for money.boolistrue/false, andcharis 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
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:?}");
}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?
When nothing tells the compiler otherwise, integer literals are i32.
2. What does 200_u8.saturating_add(100) return?
Saturating arithmetic stops at the type's maximum, which is 255 for u8.
3. What is the value of 7 / 2 when both are integers?
Integer division discards the fractional part.
4. How do you print a tuple (1, "a") so that it shows (1, "a")?
Tuples do not implement Display; the Debug format {:?} prints them with quotes around strings.
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
