Explore data types for numbers, text, and true/false values

Completed

Rust is a statically typed language. The compiler must know the exact data type for all variables in your code for your program to compile and run. The compiler can usually infer the data type for a variable based on the bound value. You don't always need to explicitly tell the type in your code. When many types are possible, you must inform the compiler the specific type by using type annotations.

In the following example, we tell the compiler to create the number variable as a 32-bit integer. We specify the data type u32 after the variable name. Notice the use of the colon : after the variable name.

let number: u32 = 14;
println!("The number is {}.", number);

If we enclose the variable value in double quotation marks, the compiler interprets the value as text rather than a number. The inferred data type of the value doesn't match the u32 data type specified for the variable, so the compiler issues an error:

let number: u32 = "14";

The compiler error:

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
 --> src/main.rs:2:23
  |
2 |     let number: u32 = "14";
  |                 ---   ^^^^ expected `u32`, found `&str`
  |                 |
  |                 expected due to this

error: aborting due to previous error

You can interact with the preceding code in this Rust Playground.

Built-in data types

Rust comes with some built-in primitive data types to express numbers, text, and truth. Several of these types are referred to as scalar because they represent a single value:

  • Integer numbers
  • Floating point numbers
  • Booleans
  • Characters

Rust also offers more complex data types to work with data series, such as string and tuple values.

Numbers: Integers and floating point values

Integers in Rust are identified by bit size and the signed property. A signed integer can be a positive or negative number. An unsigned integer can be only a positive number.

Length Signed Unsigned    
8-bit i8 u8    
16-bit i16 u16    
32-bit i32 u32    
64-bit i64 u64    
128-bit i128 u128    
architecture-dependent isize usize    

The isize and usize types depend on the kind of computer your program is running on. The 64-bit type is used on a 64-bit architecture, and the 32-bit type on a 32-bit architecture. If you don't specify the type for an integer, and the system can't infer the type, it assigns the i32 type (a 32-bit signed integer) by default.

Rust has two floating-point data types for decimal values: f32 (32 bits) and f64 (64 bits). The default floating-point type is f64. On modern CPUs, the f64 type is roughly the same speed as the f32 type, but it has greater precision.

let number_64 = 4.0;      // compiler infers the value to use the default type f64
let number_32: f32 = 5.0; // type f32 specified via annotation

All of the primitive number types in Rust support mathematical operations like addition, subtraction, multiplication, and division.

// Addition, Subtraction, and Multiplication
println!("1 + 2 = {} and 8 - 5 = {} and 15 * 3 = {}", 1u32 + 2, 8i32 - 5, 15 * 3);

// Integer and Floating point division
println!("9 / 2 = {} but 9.0 / 2.0 = {}", 9u32 / 2, 9.0 / 2.0);

Note

When we call the println macro, we add the data type suffix to each literal number to inform Rust about the data type. The syntax 1u32 tells the compiler the value is the number 1 and to interpret the value as an unsigned 32-bit integer.

If we don't provide type annotations, Rust tries to infer the type from the context. When the context is ambiguous, it assigns the i32 type (a 32-bit signed integer) by default.

You can try running this example in the Rust Playground.

Booleans: True or false

The boolean type in Rust is used to store truth. The bool type has two possible values: true or false. Boolean values are used widely in conditional expressions. If a bool statement or value is true, then do this action; otherwise (the statement or value is false), do a different action. A boolean value is often returned by a comparison check.

In the following example, we use the greater than > operator to test two values. The operator returns a boolean value that shows the result of the test.

// Declare variable to store result of "greater than" test, Is 1 > 4? -- false
let is_bigger = 1 > 4;
println!("Is 1 > 4? {}", is_bigger);  

Text: Characters and strings

Rust supports text values with two basic string types and one character type. A character is a single item, while a string is a series of characters. All text types are valid UTF-8 representations.

Characters

The char type is the most primitive of the text types. The value is specified by enclosing the item in single quotation marks:

let uppercase_s = 'S';
let lowercase_f = 'f';
let smiley_face = '😃';

Note

Some languages treat their char types as 8-bit unsigned integers, which is the equivalent of the Rust u8 type. The char type in Rust contains unicode code points, but they don't use UTF-8 encoding. A char in Rust is a 21-bit integer that's padded to be 32 bits wide. The char contains the plain code point value directly. You can learn more about the char type in Rust in the Rust documentation.

Strings

The str type, also known as a string slice is a view into string data. Most of the time, we refer to these types by using reference-style syntax that precedes the type with the ampersand &str. We'll cover references in the following modules. For now, you can think of &str as a pointer to immutable string data. String literals are all of type &str.

Although string literals are convenient to use in introductory Rust examples, they aren't suitable for every situation where we might want to use text. Not every string can be known at compile time. An example is when a user interacts with a program during runtime and sends text via a terminal.

For these scenarios, Rust has a second string type named String. This type is allocated on the heap. When you use the String type, the length of the string (number of characters) doesn't need to be known before the code is compiled.

Note

If you're familiar with a garbage-collected language, you might be wondering why Rust has two string types.1 Strings are extremely complex data types. Most languages use their garbage collectors to gloss over this complexity. Rust as a system's language exposes some of the inherent complexity of strings. With the added complexity comes a very fine-grained amount of control over how memory is used in your program.

1 _Actually, Rust has more than two string types. In this module, we cover only the String and &str types. You can learn more about the string types offered in the Rust documentation.

We won't get a full idea of the difference between String and &str until we learn about Rust's ownership and borrowing system. Until then, you can think of String type data as text data that can change as your program runs. The &str references are immutable views into the text data that don't change as your program runs.

Text example

The following example shows how to use the char and &str data types in Rust.

  • Two character variables are declared with the : char annotation syntax. The values are specified by using single quotation marks.
  • A third character variable is declared and bound to a single image. For this variable, we let the compiler infer the data type.
  • Two string variables are declared and bound to their respective values. The strings are enclosed in double quotation marks.
  • One of the string variables is declared with the : &str annotation syntax to specify the data type. The data type for the other variable is left unspecified. The compiler will infer the data type for this variable based on the context.

Notice that the string_1 variable includes an empty space at the end of the series of characters.

// Specify the data type "char"
let character_1: char = 'S';
let character_2: char = 'f';
   
// Compiler interprets a single item in quotations as the "char" data type
let smiley_face = '😃';

// Compiler interprets a series of items in quotations as a "str" data type and creates a "&str" reference
let string_1 = "miley ";

// Specify the data type "str" with the reference syntax "&str"
let string_2: &str = "ace";

println!("{} is a {}{}{}{}.", smiley_face, character_1, string_1, character_2, string_2);

Here's the output for our example:

😃 is a Smiley face.

What happens if we don't specify the ampersand & before str in this example? To find out, try running this example in the Rust Playground.

Check your knowledge

Answer the following questions to see what you've learned. Choose one answer for each question, and then select Check your answers.

1.

Which statement describes how integer number values are defined in Rust?

2.

Which statement correctly describes how Rust supports text character values?