Scalar & Compound Types
Rust's scalar types hold single values. Compound types - tuples and fixed-size arrays - group values with statically known shapes.
Recipe
fn main() {
let count: u32 = 42;
let ratio: f64 = 3.14;
let ok: bool = true;
let ch: char = '✓';
let pair: (u32, &str) = (count, "items");
let nums: [i32; 3] = [1, 2, 3];
println!("{count} {ratio} {ok} {ch} {:?} {:?}", pair, nums);
}When to reach for this: Choosing the right primitive or small aggregate before reaching for Vec or custom structs.
Working Example
fn stats(values: [f64; 4]) -> (f64, f64) {
let sum: f64 = values.iter().sum();
let avg = sum / values.len() as f64;
(sum, avg)
}
fn main() {
let readings = [98.6, 99.1, 98.4, 99.0];
let (total, average) = stats(readings);
println!("sum={total:.2}, avg={average:.2}");
}What this demonstrates:
- Fixed-size array passed by value (copies 4
f64s on the stack) - Tuple return type for multiple values before a struct is warranted
as f64for explicit numeric cast- Array indexing with compile-time-known length
Deep Dive
Scalar Types at a Glance
| Type | Size (typical) | Notes |
|---|---|---|
i32 / u32 | 4 bytes | Default integer type |
i64 / u64 | 8 bytes | Timestamps, IDs |
f32 / f64 | 4 / 8 bytes | f64 is default float |
bool | 1 byte | Only true or false |
char | 4 bytes | Unicode scalar, not ASCII-only |
Integer Literals and Overflow
let hex = 0xff_u8;
let bin = 0b1010_0110;
let wrapped = 255u8.wrapping_add(1); // 0In debug builds, overflow panics. Use wrapping_*, checked_*, or saturating_* when overflow behavior matters.
Arrays vs Slices
let arr: [i32; 3] = [1, 2, 3];
let slice: &[i32] = &arr[1..];Arrays have length in the type. Slices are fat pointers (pointer + length) and can refer to arrays, Vec, or stack data.
Tuples
let t = (1, "a", true);
let (a, b, c) = t;
assert_eq!(t.0, a);Empty tuple () is the unit type - return type of functions that return nothing meaningful.
Gotchas
- Assuming
charis one byte -charis UTF-32. Fix: Useu8for raw bytes; iteratestrwith.chars(). - Indexing past array bounds - Runtime panic in safe Rust. Fix: Use
.get(i)for optional access. - Mixing integer types -
let x: i32 = 1i16;needs explicit cast. Fix: UseasorTryInto. - Float equality -
0.1 + 0.2 == 0.3is false. Fix: Compare with epsilon or use integers (cents). - Large arrays on the stack -
[u8; 1_000_000]can overflow the stack. Fix: UseVecorBox<[T]>on the heap.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Struct with named fields | Tuple has more than 2-3 fields or unclear meaning | One-off (x, y) coordinates |
Vec<T> | Runtime-sized collection | Length is fixed and small |
| Newtype wrapper | Distinct units (Meters, Seconds) | Simple internal math only |
SmallVec / array | Usually small, occasionally large | Always heap-allocated data |
FAQs
What is the default integer type?
i32 when inference has no other constraint. Use explicit suffixes (42u64) in APIs that need a specific width.
Can array size be generic?
Yes, with const generics: struct Buffer<const N: usize> { data: [u8; N] }.
How do I initialize an array with the same value?
let a = [0u8; 1024]; repeats 0u8 1024 times. The element must implement Copy.
Are bools one bit?
No. bool is 1 byte for alignment and simple addressing.
Can tuples implement traits?
Only for specific arities via standard impls (e.g. Clone for small tuples). Prefer structs for public APIs.
What is `isize`?
Pointer-sized signed integer. Use for indexing and len() results (usize).
How do I convert strings to numbers?
"42".parse::<i32>() returns Result. See the strings section for FromStr.
Does Rust have a `null`?
No. Use Option<T> for absent values.
Can I compare arrays?
Yes, if T: PartialEq. [1,2] == [1,2] works element-wise.
When should I use `f32`?
GPU/math kernels where memory bandwidth matters. Default to f64 elsewhere.
Related
- Variables, Mutability & Shadowing - bindings and destructuring
- References & Slices - slice types in depth
- The Type System & Inference - inference rules
- Structs Basics - named product types
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.