Variable
6 minutes of reading
In this chapter, we will introduce how variables work in Rust and some of the basic variable types.
In Rust, all variables are immutable by default.
let i = 4; // this creates a variable storing the value 4
i = 5; // this attempts to assign the value 5 to the variable however it gives an error
In order to make variables mutable, you need to use the mut keyword.
let mut i = 4;
i = 5; // this is okay
The reason that the Rust team makes this decision of making all variables immutable by default is it can encourage developers to write cleaner code. How is that possible? That comes down to the concept of shadowing.
In Rust, we can declare a new variable with the same name as a previous variable, and the new variable shadows the previous variable, that is, the previous variable will no longer be accessible in the current block of code (a block of code in Rust is defined as a bunch of codes that are enclosed by a pair of curly brackets).
1
2
3
4
5
6
7
8
9
10
11
fn main() {
    let x = 10;
    {
        let x = 5;
        println!("x: {}", x); // this substitutes the value of x into {}
    }
    let x = "I'm a string slice";
    println!("x: {}", x);
    let x = true;
    println!("x: {}", x);
}
If you run the above program, the output will be
x: 5
x: I'm a string slice
x: true
So, yeah that's cool but how is that useful? Have you ever dealt with a program with hundreds or thousands of lines of code in one file? A variable in these gigantic programs sometimes is used in a line that is far away from the line where the variable was declared. This would make it harder to understand the code.
let mut counter = 10;
// ...
// thousands lines of code
// ...
counter = 5;
Also, sometimes you may want to declare a new variable that serves the same purpose as a previously declared variable, however it may be stressful for you to come up with another variable name that is suitable so eventually you decided to name your variables using some cryptic naming ways.
let mut counter_1 = 10;
// some logic
let mut counter_2 = 5;
// some logic
Instead, you could just make use of shadowing by declaring a variable with the same name as the previous variable. That saves you time on choosing variable names and also makes the logic that utilizes the variable and the variable declaration come closer together, thus producing better and more understandable code.
let mut counter = 10;
// some logic
let mut counter = 5;
// some logic
Now, let's discuss a little bit about variable types. Normally when you declare and initialize a variable at the same time you don't have to specify the variable type, the compiler will infer it for you.
let x = 12;
However, suppose you want to specify it, you can do it this way.
let x: i32 = 12; // this stores a signed 32-bit integer
You can also declare a constant, which is always immutable, using the const keyword. You must specify the variable type when declaring a constant.
Notice the 100_000_000 syntax is not necessary, you can safely use 100000000 and it will still work. That's just a cleaner way to write an integer with lots of digits, you can even use this syntax for non-constants!
const SOME_CONSTANT: i32 = 100_000_000;
Now we understand the foundation of how variables work in Rust, let's now dive into some of the most common variable types.
Integer
Integers in Rust can be 8, 16, 32 or 64 bits and they can either be signed or unsigned.
Without specifying the type, an integer is inferred by the compiler as i32, i.e. a 32-bit integer that can store both positive and negative values.
let x = 12; // equivalent to let x: i32 = 12;
let x: u32 = 15; // 32-bit unsigned integer
let x: i8 = -127; // 8-bit signed integer, this can store integers between -128 and 127
Float
There are two float types, f32 and f64. By default a float is f64, i.e a 64-bit floating point.
let x = 2.3; // equivalent to let x: f64 = 2.3;
let x: f32 = 1.5;
Char
A char stores a single 8-bit character.
let x = 'c'; // equivalent to let x: char = 'c';
Boolean
let x = true; // equivalent to let x: bool = false;
let x = false;