Scalar and compound types

Rust · Essentials · 12 min

Rust never converts numbers implicitly, so knowing the primitive types saves you from most early compile errors.

Rust has four scalar types. Integers come in signed i8 to i128 and unsigned u8 to u128, plus isize/usize, which are pointer-sized; usize is the type of indices and lengths. The default integer is i32, the default float is f64 (f32 also exists). bool is true/false, and char is a 4-byte Unicode scalar value written 'a' or 'é', not a byte.

There are no implicit conversions. Adding an i32 to a u64 is a compile error; convert explicitly with as (which truncates or wraps silently) or with From/TryFrom (u8::try_from(300) returns an Err). Prefer try_from when a value might not fit.

Integer overflow panics in debug builds and wraps in release builds. When wrapping or saturation is what you mean, say so: wrapping_add, saturating_sub, checked_mul (returns Option).

Two built-in compound types group values:

  • A tuple (i32, &str, bool) holds a fixed number of values of different types. Access fields with t.0, t.1, or destructure with let (a, b, c) = t;. The empty tuple (), called unit, is what functions return when they return nothing.
  • An array [u8; 4] holds a fixed number of values of one type on the stack. [0; 256] repeats a value. Indexing is bounds-checked: arr[10] on a 4-element array panics instead of reading random memory.

For a growable list you will use Vec<T>, covered later.

Example

fn main() {
    let big: u64 = 10_000_000_000;
    let small: i32 = -7;
    // let sum = big + small;            // error: mismatched types
    let sum = big as i64 + small as i64; // explicit conversion
    let byte = u8::try_from(300).ok();   // None: does not fit
    let capped = 250u8.saturating_add(10); // 255

    let c = 'é';                         // a char is a Unicode scalar value
    let point: (f64, f64) = (1.5, -2.0);
    let (x, y) = point;                  // destructure a tuple
    let grid = [0u8; 4];                 // [0, 0, 0, 0]

    println!("{sum} {byte:?} {capped} {c} {x} {} {}", y, grid.len());
}

Remember

  • Default numeric types are i32 and f64; usize is for indices and lengths
  • No implicit numeric conversions: use as, From or TryFrom
  • Overflow panics in debug and wraps in release unless you use the explicit methods
  • Tuples mix types; arrays have a fixed length and bounds-checked indexing

Watch out for

  • Using as for narrowing conversions and silently truncating values; use try_from when it might not fit.

Check yourself

  1. What does `300_i32 as u8` evaluate to?
  2. Which type is used for vector and array indices?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: doc.rust-lang.org/book/ch03-02-data-types.html