2.8 Primitive Types

A primitive type is for instance what an i32 is. Using Rust you don’t only have the ability to make use of one primitive type, but rather a whole list of them.

Whilst reading through these paragraphs, it’s highly suggested you open the Rust documentation to view all of its primitive Types.

Numeric types

These include the signed integers, an i followed by the number of bits the type can store; the unsigned integers, a u followed by the number of bits the type can store; the floats, an f followed by the number of bits the type can store—as well as isize and usize.

Signed integers

Signed integers make use of their data to store both positive and negative numbers—meaning that half of its storage capacity is used for its negative side, whilst the rest is used for its positive.

Unsigned integers

Unsigned integers have no negative side, which means that it has a maximum of twice as high as signed integers. Since it can’t be negative, it’s technically the same size as a signed integer, only it can count twice as high, but nowhere near as low—considering it can’t access negative numbers at all.

Floating-point numbers

Floats—or floating-point numbers—are usually used in calculations that needs be somewhat fast. They have a precision that is not as good as that of the other numeric types. They correspond to the industry standard IEEE-754 single and double precision numbers.

Fixed-size types

Both the floats, signed- and unsigned integers are fixed-size types. This means they have a predetermined maximum size. Integers start off with an i, followed by an 8, 16, 32 or 64. The same goes for unsigned integers, although they start off with a u. Floats only have two sizes: 32 and 64—appended onto an f.

Variable-size types

Variable-size types, isize and usize have a size dependent on the machine architecture, but are always of sufficient size to be set to the size of any collection.

Booleans

Booleans can either be true or false, which in theory means it only needs to occupy one bit (1 or 0) in order to be stored in memory, as it only has two different possible states.

let x = false;

Because of Rust’s type inference there’s no need for us to actually specify that this variable binding is of the boolean type, the compiler can figure out that on its own. You may, however, specify its type should you for some reason feel like it.

let x: bool = true;

Rust will—like once before—thank you for being so extremely clear in what it is you want!

As you can see, the way you write the type of boolean in Rust, is using the keyword bool.

Chars

The char in Rust represents a single character, which unlike some languages that use one byte to store their characters, always require four bytes of storage. Every Rust character is encoded as one Unicode scalar value. A char is written using single ticks ':

let c = 'o';

Since we have four bytes of storage, we have access to the whole Unicode table. This means that we can create characters like this:

let c = 'আ';

Depending on the capabilities of your console, you may or not be able to print and view a character such as this. Other than that, printing characters to the console works the exact same way as printing any variable out:

let c = 'আ';

println!("Hello, {}!", c);

If we’re lucky, the output upon compiling and executing will be this:

String type

There’s a string type in Rust called str. This is Rust’s most primitive string type, and is by no means useful on its own. In order for it to be reasonably useful, we need to prepend str with an &, making it a reference &str—references are covered in the section Borrowing. This type of string is usually referred to as a ‘string slice’.

We can create string slices in our code using ":

let greetings = "Hello, world!"; // greetings is of type ‘&str’

println!("{}", greetings);

Both this type of string and a different higher-level string are discussed in the section on /rust/strings. Something to notice regarding string slices is that they always are of valid UTF-8—meaning they sometimes may require multiple chars to make up one letter or symbol.

Arrays

Arrays are representations for a sequence of things—a storage for multiple values, or elements, held together inside a structure.

let x = [4, 5, 6];

Subscript notation

You can access an element from any array using Rust’s subscript notation [] syntax:

let x = [4, 5, 6];

println!("{}", x[1])

This will print 5, due to the fact that the count for accessing elements in Rust starts at zero. The zeroth element thus corresponds to the first position in our array.

Should you want to access an element using a variable rather than inputting the number manually, the variable requires to be of variable-size type—to be exact, of type usize.

Initialization shorthand

You can create a long array using an array creation shorthand:

let x = [0; 34];

This will create an array containing thirty-four elements—every element with its value set to zero.

Length

The length of an array can be retrieved by calling the len method on the array:

let x = [0; 21];

println!("{}", x.len);

This will print 21, since the length of the array structure is in fact twenty-one; the array x contains twenty-one elements—numbers to be precise.

Mutability

The entire array structure is either mutable or immutable. Like all let bindings, the array is immutable by default. In order to make it mutable, simply add the mut keyword.

Moreover

An array can contain pretty much anything. All the elements do however need to be of the same type. We can for instance create an array containing string slices:

let animals = ["Monkey", "Rhino", "Hippo"];

The elements can still be accessed using subscript notation.

If we wanted to, we could even initialize an array using the array shorthand:

let a = ["Hello, world!"; 20];

This array in particular contains twenty elements, all set to string slices with the value of “Hello, world!”

Slices

Slices are used as a means of viewing only selected elements of a structure.

let x = [0, 1, 2];

let big_slice = &x[..]; // This slice contains all the elemets from x
let small_slice = &x[1..2]; // This slice contains only elements one and two

You can see in the above code that a slice is sort of a viewport into—for instance—an array.

Tuples

Tuples are ordered list—ordered lists that are fixed in size. They work in a similar fashion to that of arrays, but have the ability to store different types of elements for each position.

let x = ("Hello, world!", 5, 3.2); // x: (&str, i32, f32)

Tuples may have any length, but can’t have their lengths changed after being created. This is because a tuple has its very own specific type and to change the length of one you would end up changing its type.

Accessing elements

The elements of a tuple may be accessed using a single period .:

let x = ("Hello, world!", 3);

println!("{}", x.0);

This will print out:

Hello, world!

The x.0 is in this case referring to the zeroth index inside the tuple. Go ahead and increase this number in order to access a different element.

Destructuring

You may also destructure tuples using patterns—in the same way that we defined multiple values using let earlier, the right-hand side of the equal sign is actually a tuple:

let x = ("Food time!", 4);
let (a, b) = x;

println!("{}", b);

The above example will print out 4.

The amount of variables contained inside the pattern and amount of variables inside the tuple need to correspond with one another.

Assigning into

Similarly to how you can destructure a tuple using let, you may assign one tuple into another if they have the same arity—the same length and types:

let mut x = ("Food time!", 4);
let y = ("Hurrah!", 3);

x = y;

println!("{}", b);

The above example will print 3.

Exercises

  • Create a string slice containing “Hello, ” and another containing “world!”. Print them out using the same println! to form “Hello, world!”.
  • Mess around with different types of data presented within this section and attempt to memorize them all. This exercise is number one on the list when you are bored, although not scientifically proven.

Moreover

You’re still here? That’s quite amazing—I’m almost not. There’s even more to the primitive types of Rust, but once you know all the ones contained here, the rest will not be anywhere near as difficult to add onto that human memory of yours.

Loops