A Trip to Data Types in Rust
Tags: rust, tutorial, programming, learning
Published: 2020-07-18 09:38:15

*This post published on my blog [before](https://aligoren.com/a-trip-to-data-types-in-rust/)*

-----

Hi everyone. Before that, I wrote a post called [Understanding Variables and Mutability in Rust](https://dev.to/aligoren/understanding-variables-and-mutability-in-rust-4p7b).

Today we'll review Data Types in Rust. In this small story, we'll see scalar and compound data type subsets.

Before starting, I'll create a project with cargo;

```bash
cargo new data_types

cd data_types
```

## Introduction

Rust is a **statically typed** programming language. This means the compiler must know the data type of the variable's value you assigned.

The compiler can understand what type we want to use based on the value. In some cases, many types can be possible. For example, you got string value but it actually should be a numerical value. You can parse it. So, in this case, we have two different data types. But we'll only one.

```rust
let user_age_from_input: i8 = "27".parse().expect("Not a number!");
```

If we didn't add the type annotation, we'll see an output like

```text
error[E0282]: type annotations needed
 --> src/main.rs:4:9
  |
4 |     let user_age_from_input = "27".parse().expect("Not a number!");
  |         ^^^^^^^^^^^^^^^^^^^ consider giving `user_age_from_input` a type
```

## Scalar Types

Scalar types represent only one value. Currently, we have four primary scalar types in Rust. These are **integers**, **floating-point numbers**, **booleans** and **characters**. You may familiar with them from other programming languages.

## Integer Types

![Integer Types](https://dev-to-uploads.s3.amazonaws.com/i/6oy77gz0x9x6q98axth3.png)

While we're talking about the integers, we actually saying is a number without a [fractional part](https://en.wikipedia.org/wiki/Fractional_part).

You can declare integer variables using `i` or `u` symbols. These are representing signed and unsigned types.

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

Each type has an explicit size in Rust. When you define a signed variable, this also means that variable can store negative and positive values.

Unsigned values can't store negative values. If you try to assign a negative value to an unsigned variable you'll see an error.

```rust
let user_age_from_input: u8 = -8;
    
println!("My age is {}", user_age_from_input);
```

You'll see this output;

```bash
error[E0600]: cannot apply unary operator `-` to type `u8`
 --> src/main.rs:4:35
  |
4 |     let user_age_from_input: u8 = -8;
  |                                   ^^ cannot apply unary operator `-`
  |
  = note: unsigned values cannot be negated
```

A negative value can be bind in run time. But this isn't our topic for now.

### When To Use Unsigned Variable?

In short, when your numbers start from 0...to positive numbers.

### When To Use Signed Variable?

In short, when you need to store negative or positive numbers in some processes such as calculations, use a signed variable.

### Which Type Should I Use?

![Which Type Should I Use?](https://dev-to-uploads.s3.amazonaws.com/i/i8owldt6woq17hemjwvq.png)

If you don't know which type you should use, don't worry. Default data type is `i32` in Rust.

```rust
let my_age = 27; // i32 by default
```

### Calculations of the Maximum Number for Variants

![Calculations of the Maximum Number for Variants](https://dev-to-uploads.s3.amazonaws.com/i/9gb8wwvurgvfwtkie39b.png)

By default you use i32. But how many numbers you can store? If you can't say that you can calculate like that;

**For Signed Variants**

from -(2<sup>n-1</sup>) to 2(<sup>n-1</sup>)-1 where **n** is the number of bits that variant uses.

**For Unsigned Variants**

from 0 to 2(<sup>n-1</sup>)-1.

There are two types that depend on the kind of computer your program is running on 64 bits or 32 bits. These are `isize` and `usize`

### Integer Overflow

![Integer Overflow](https://dev-to-uploads.s3.amazonaws.com/i/92uo0m4xi5vmbwzcdy71.png)

Let's assume you have a variable type that declared as u8. So, it can hold values between 0 and 255. If you try to change that variable's value with 256, integer overflow will occur.

If you compile your program in debug mode, Rust will check your program for integer overflow cases and your program will `panic` at runtime.

When you're compiling your program in release mode with `--release` flag, Rust won't check your program for integer overflow. Instead of that, Rust will perform two’s *complement wrapping*

> In short, values greater than the maximum value the type can hold “wrap around” to the minimum of the values the type can hold. In the case of a `u8`, 256 becomes 0, 257 becomes 1, and so on. The program won’t panic, but the variable will have a value that probably isn’t what you were expecting it to have. Relying on integer overflow’s wrapping behavior is considered an error

## Floating-Point Types

Rust has two primitive types for floating-point numbers. These are numbers with decimal points. While `i32` is the default type in integers, `f64` is the default type for floating-point numbers in Rust.

```rust
let amount = 35.50; // f64

let total: f32 = 400.45; //f32 
```

> Floating-point numbers are represented according to the IEEE-754 standard. The `f32` type is a single-precision float, and `f64` has double precision.

## Numeric Operations

Rust supports the basic mathematical operations like the other programming languages. Addition, subtraction, multiplication, division, and remainders are supported. This just an example;

```rust
// addition
let sum_example = 19 + 1;

// subtraction
let get_difference = 2020 - 1993;

// multiplication
let multiply_it = 10 * 2;

// division
let divide_it = 30 / 2;

// remainder
let get_mod = 10 % 2;
```

## Boolean Type

![Boolean Type](https://dev-to-uploads.s3.amazonaws.com/i/90j0axug2b5m004leksw.png)

**Is there anything except true or false in the world?**

Rust also has a boolean type like the other programming languages. There are two possible values in Rust. These are `true` or `false`.

```rust
let is_completed = false;

// explicit type annotation
let is_send: bool = true;
```

## Character Type

We've been worked on numbers and bool types till now. Now, we'll see a different type called character. 

```rust
let symbol = '₺';

println!("Symbol is {}", symbol); // Symbol is ₺
```

> Rust’s char type is four bytes in size and represents a Unicode Scalar Value, which means it can represent a lot more than just ASCII. Accented letters; Turkish, Chinese, Japanese, Korean, and Emojis. In the next posts, we'll see the string types.

## Compound Types

Compound types can group multiple values into one type. Rust has two primitive compound types. These are `tuple` and `arrays`.

## Tuple Type

A tuple type is a way to grouping the values in various types into one compound type. When you declare a tuple type, you can't add a new value. They have fixed lengths. (*Actually there are few tricks but there is no built-in way*).

```rust
let user_tuple: (i32, f64, bool) = (2019, 1500.76, true);

// or

let user_tuple = (2019, 1500.76, true);
```

This was a simple tuple. We created a tuple by writing a comma-separated list of values inside parentheses. Each position in the tuple has a type, and the types of the different values in the tuple don’t have to be the same. We can print-out it like that;

```rust
let user_tuple = (2019, 1500.76, true);

println!("User tuple is {:?}", user_tuple);
```

### Destructing

You can destruct the values in tuples. If you're used JavaScript before, you may familiar with destructing.

```rust
let user_tuple = (2019, 1500.76, true);

let (register_year, balance, is_customer) = user_tuple;

println!("User's balance is: {}", balance);
```

### Accessing Tuple Indexes

You can access a tuple element directly using a dot (`.`).

```rust
let user_tuple = (2019, 1500.76, true);

println!("User's balance is: {}", user_tuple.1);
```

This could be annoying :)

## Array Type

The other way of storing multiple values is by using arrays. You can use different types in tuples. Unlike a tuple, each value of an array must be the same. Rust's array behavior is different than some other programming languages. Because arrays have a fixed length in Rust. For example;

```rust
let users = ["Ali", "Ben", "Burak", "Enes"];
    
println!("Users: {:?}", users);
```

> Arrays are useful when you want your data allocated on the stack rather than the heap or when you want to ensure you always have a fixed number of elements.

An array isn't like a vector type. A vector is a similar collection type with an array. Its provided by the standard library and it's allowed to grow or shrink in size.

This is a good use case example for arrays from Rust's official documentation

```rust
let months = ["January", "February", "March", "April", "May", "June", "July",
              "August", "September", "October", "November", "December"];
```

You don't need to add a new element to months array. Because there are 12 months in a year.

You would want to create arrays with types.

```rust
let numbers: [i32; 5] = [1, 2, 3, 4, 5];
/*------type: ^^^--size--*/

println!("Numbers: {:?}", numbers);
```

or

```rust
let numbers = [1; 5];
/*------value: ^--size--*/

println!("Numbers: {:?}", numbers);
```

In this way, you can create an array that has 5 elements containing number 1.

### Accessing Array Elements

As in other languages, you can access array items. The first element's index is zero. The index increases one by one.

```rust
let numbers: [i32; 5] = [1, 2, 3, 4, 5];

println!("First element is: {}", numbers[0]);
```

If you try to access an invalid index, your program won't compile.

```rust
let numbers: [i32; 5] = [1, 2, 3, 4, 5];

println!("Number is: {}", numbers[5]);
```

```bash
error: this operation will panic at runtime
 --> src/main.rs:6:29
  |
6 |     println!("Number is: {:?}", numbers[5]);
  |                             ^^^^^^^^^^ index out of bounds: the len is 5 but the index is 5
  |
  = note: `#[deny(unconditional_panic)]` on by default
```

You will see an error as the above output. Because Rust will check that the index you’ve specified is less than the array length. If the index is greater than or equal to the array length, Rust will panic.

That's all.

Thanks for reading :)

## Resources

- [Data Types](https://doc.rust-lang.org/book/ch03-02-data-types.html)