Variables in Rust
Variables is one of the most crucial concepts in programming, they are used to store data in memory, so that later they can be accessed, modified, and used in different parts of the program.
In Rust, variables are immutable by default, which means that once you've assigned a value to a variable, you can't change it later.
This is a way that Rust pushes you to write code that's more predictable and easier to reason about, so that when a variable is sent to another thread, it is guaranteed that the value of the variable won't change, therefore the other threads can safely assume the variable is not going to change. This feature contributes to the safety and concurrency of Rust, that's not prevalent in other low-level programming languages.
However, we are used to changing values of variables after they've been declared, otherwise, writing code would be very hard and inconvenient. Hence, Rust provides you a way to make variables mutable, which means that you can change the value of a variable after it's been declared, but you have to explicitly declare the variable as mutable by using the mut
keyword.
But when it comes to the choice between using mutable or immutable variables, it's preferred to use immutable variables whenever possible for performance optimizations. But this is not a strict rule, you can use mutable variables when you need to change the value of a variable after it's been declared whenever it's more convenient.
Let's have some examples to illustrate the concepts we've just discussed.
Create a new project using Cargo:
Declaring local variables
You can declare a local variable in Rust using the let
keyword. Here's an example:
In the code above, the value of x
is set to 5
and it's immutable by default, which means you can't change the value of x
.
Let's try to change the value of the variable x
and see what the compiler is going to say:
Can't change immutable variable Rust compile error
As you can see, the Rust compiler gives a really nice error message that tells us exactly what's wrong with our code and gives us a hint on how to fix it.
cannot assign twice to immutable variable x
, this error message tells us that we're trying to reassign a value to an immutable variable, which is not allowed in Rust.
In order to fix it, we can either remove the reassignment of the variable x
or we can make the variable x
mutable by using the mut
keyword.
Let's fix the code by removing the reassignment of the variable x
:
The code should compile and run without any errors. Let's run the code and see the output:
Immutable variables in Rust
You can not change the value of an immutable variable after it's been declared, without explicitly telling the compiler that the variable is mutable by using the
mut
keyword.
Mutable variables
Even though immutability has its own benefits and encouraged in Rust, there are cases in which using mutable variables is more convenient and easier to write. Rust let's you turn a variable into a mutable one by using the mut
keyword.
Let's make the variable x
mutable and try again:
Let's run the code and see the output:
Mutable variable Rust output
You can change the mutability of a variable by using the
mut
keyword. Once a variable is declared as mutable, you can change its value as many times as you want.
Shadowing
Unlike most programming languages, Rust allows you to declare multiple variables with the same name, but once a new variable is declared with the same name, the old variable is shadowed (it goes out of scope) and the new variable is used instead.
Let's demonstrate this with an example:
In the example above, we've declared a variable x
with the value 5
, then we've declared a new variable x
with the value x + 1
, in this case the first value of x
is used for the x + 1
operation and then assigned to a different variable x
, in that case the old variable x
is shadowed and the new variable x
is used instead which is the result of the x + 1
operation.
Same thing happens with the next line, in this case the value of x
is 6
and it's multiplied by 2
and assigned to a new variable x
, in this case the old variable x
is shadowed and the new variable x
is used instead. So the final value that should be printed is the last variable of x
which results from the x * 2
operation.
Let's run the code and see the output:
Mutable variables in Rust
Constants
The let
keyword let's us declare local variables that belong to a specific scope, but what if we want to have a global variable that can be accessed from any part of the program?
You can't declare a variable outside a function using the let
keyword. In this example below, you'll get a compile error if you try to declare a variable outside the main
function:
Variable declared outside a function in Rust
The compiler gives us a message that says: "Consider using const
or static
instead of let
for global variables".
Rust gives us another way to declare global variables, which is by using the const
keyword. Here's an example of how to declare a constant in Rust:
Constant variables are not just immutable by default, they're only immutable and you can't use the mut
keyword with them, and the type of the variable will not be inferred by the compiler, so you must always annotate the type of the constant.
Once a value is declared using the const
syntax, it will be inlined directly in the program's binary, this means it will be stored neither in the stack nor the heap during runtime. This is a very important concept to understand, we'll discuss how Rust manages memory and stores variables in more details in the upcoming chapters.
Don't worry if you don't quite understand the concepts like stack, heap and type annotations yet, we'll discuss them in more detail later.
In the example above when we declared the constant MAX_POINTS
, we've used the type annotation u32
to tell the compiler that the constant is an unsigned 32-bit integer, we will discuss all the different data types in Rust in the next chapter.
In Rust we can use the underscore _
to separate the digits in a number to make it more human-readable, in this case it is the same as writing 100000
(one hundred thousand).
Let's write some more examples and print them to the console:
Let's run the code and see the output:
Constants in rust
In the example above, we've declared three constants, MAX_POINTS
, PI
, and AUTHOR
. The first constant is an unsigned 32-bit integer with the value 100_000
, the second constant is a 32-bit floating-point number with the value 3.14
, and the third constant is a string slice with the value "John Doe"
.
You can declare local variables (inside functions) with the
let
keyword. For global variables, you can use theconst
keyword instead.
Scope
In Rust, each variable has a specific scope in which it's valid and can be accessed. The scope of a variable is determined by the block in which it's declared. A block is a piece of code that's surrounded by curly braces {}
.
Whenever a variables goes out of scope, it means that the variable has been dropped, meaning that the memory that was allocated for the variable is freed and the variable can't be accessed anymore.
Let's have an example to illustrate the concept of scope in Rust:
Scope example in rust
In the example above, we've declared two variables x
and y
. The variable x
is declared in the outer block and it's valid from the point it's declared until the end of the function. The variable y
is declared in the inner block and it's valid from the point it's declared until the end of the inner block.
When the inner block ends, the variable y
goes out of scope and is dropped, and we can't access it anymore. The variable x
is still in scope and we can access it from the outer block.
If we tried to access the variable y
outside the inner block, we would get a compile error because the variable y
is not in scope anymore.
Variable out of scope in rust
The Rust compiler tells us that the variable y
is declared in the same function but in a different scope, and it's not accessible from the outer scope, which is quite an accurate error message.
In the next lesson, we're going to learn about the different data types in Rust and how you can give types to your variables.