Scalar Types

We’ve seen that every value in Rust has a type of some kind. There are a number of types which are built into the language itself. First, we’ll take a look at ‘scalar’ types, that is, types which represent a single value.

Remember, you can rely on type inference to figure out the type of a binding, or you can annotate it explicitly:

fn main() {
    let x: i32 = 5;
}

Integers

You’ve already seen one primitive type: i32. There are a number of built-in number types in Rust.

Here’s a chart of Rust’s integer types:

signed unsigned
8-bit i8 u8
16-bit i16 u16
32-bit i32 u32
64-bit i64 u64
arch isize usize

We have both signed and unsigned variants of numbers, and each variant has an explicit size. Unsigned numbers are never negative, and signed numbers can be positive or negative. (Think ‘plus sign’ or ‘minus sign’: that’s a signed number.) Signed numbers are stored using ‘two’s complement’ representation.

Finally, isize and usize are different sizes based on the kind of computer your program is running on. If you are on a 64-bit architecture, they are 64 bits, and if you’re on a 32-bit one, they’re 32 bits.

So how do you choose from all these options? Well, if you really don’t know, the defaults are a good choice: integer types default to i32. The primary use case for isize/usize is when indexing some sort of collection. We’ll talk more about our first collection, arrays, in just a moment.

Floating-point numbers

Rust also has two primitive floating-point numbers: f32 and f64. They are 32 bits and 64 bits in size, respectively. The default is f64.

fn main() {
    let x = 2.0; // f64

    let y: f32 = 3.0; // f32
}

Floating-point numbers are represented according to the IEEE-754 standard. f32 is a single-precision float, f64 is double-precision.

Numeric operations

Rust supports the usual operations you’d expect on all of these number types:

fn main() {
    // addition
    let sum = 5 + 10;

    // subtraction
    let difference = 95.5 - 4.3;

    // multiplication
    let product = 4 * 30;

    // division
    let quotient = 56.7 / 32.2;

    // modulus
    let remainder = 43 % 5;
}

Booleans

Somewhat fundamental to all computing, Rust has a boolean type, bool, with two possible values:

fn main() {
    let t = true;
    let f: bool = false; // with explict type annotation
}

The main way to consume boolean values is through conditionals like if, which we’ll see later in the chapter.

Characters

We’ve only worked with numbers so far, but what about letters? Rust’s most primitive alphabetic type is the char:

fn main() {
   let c = 'z';
   let z = 'ℤ';
}

Rust’s char represents a Unicode Scalar Value, which means that it can represent a lot more than just ASCII. ‘Character’ isn’t really a concept in Unicode, however: your human intutition for what a ‘character’ is may not match up with a char. It also means that chars are four bytes each.