Variables, Shadowing, and Constants in Rust
Francesco Ciulla
Posted on January 16, 2024
Understanding Variables in Rust
Rust is a statically and strongly typed language. This means that the compiler must know the type of all variables at compile time. The compiler can usually infer what type we want to use based on the value and how we use it. In cases when many types are possible, we must add a type annotation.
In this article, we will learn about the following:
- variables and mutability
- shadowing
- constants
If you prefer a video version
Variables and mutability
Let's start with understanding variables and mutability.
Let's declare a variable and print it:
fn main() {
let x = 5;
println!("x is {}", x);
}
Here, we are using string interpolation to print the value of x. The curly braces are placeholders for the value of x. The value of x is then passed as an argument to println!().
You should see the following output:
This is an implicit declaration of type, but at compile time, Rust infers the type of x to be i32. This is because we assigned the value 3 to x, and integer literals default to i32 unless otherwise specified.
If you want to declare the variable type explicitly, you can use a type annotation, which is the colon (:) followed by a type name.
Let's try something that seems we can do but we actually can't:
fn main() {
let x = 5;
println!("x is {}", x);
x = 6;
println!("x is {}", x);
}
In Rust, variables are immutable by default. This means that once we assign a value to a variable, we can't change it. If we try to change the value of a variable, we get an error:
the mut keyword
If we want to change a variable's value, we must declare it as mutable. We do this by adding the mut
keyword before the variable name, as shown here:
fn main() {
let mut x = 5;
println!("x is {}", x);
x = 6;
println!("x is {}", x);
}
Now we can change the value of x without getting an error.
Shadowing
Now, let's see the concept of shadowing.
We can change the value of an immutable variable by shadowing it. Shadowing is when we declare a new variable with the same name as a previous variable. The new variable shadows the previous variable, and we can assign a new value to the new variable while the old variable remains unchanged.
fn main() {
let x = 5;
println!("x is {}", x);
let x = 6;
println!("x is {}", x);
}
You might think that we would get an error here, but we don't. This is because we are declaring a new variable, not changing the value of the old one. The old variable is still there, but the new variable shadows it. We can't use the old variable anymore, but we can use the new one.
I can even use the previous variable's value to initialize the new variable:
fn main() {
let x = 5;
println!("x is {}", x);
let x = x + 2;
println!("x is {}", x);
}
Name shadowing in a different scope
Name shadowing is when we declare a new variable with the same name as a previous variable. The new variable shadows the previous variable, and we can assign a new value to the new variable while the old variable remains unchanged.
Let's see an example:
fn main() {
let x = 5;
println!("x is {}", x);
{
let x = 8;
println!("x is {}", x);
}
let x = x + 2;
println!("x is {}", x);
}
Changing the type
When you use shadowing, you can change the type of a variable. This is because you are declaring a new variable, not changing the value of the old one.
You can use it to change the type of a variable:
fn main() {
let x = 5;
println!("x is {}", x);
let x = "hello world!";
println!("x is {}", x);
}
But you can't change the type of a variable without shadowing it:
fn main() {
let mut x = 5;
println!("x is {}", x);
x = "hello world!";
println!("x is {}", x);
}
We will get an error.
This means that if we use shadowing, we can change the type, but if a variable is mutable, we can't change the type without shadowing it.
Constants Example
Constants are variables that are immutable and have a fixed value.
They are declared using the const
` keyword. They must be annotated with a type, and they can only be set to a constant expression, not the result of a function call or any other value that could only be computed at runtime.
rust
fn main() {
const MAX_LEVEL:i32 = 100_000;
println!("The maximum level is: {}", MAX_LEVEL);
}
Recapt for constants:
- you must declare the type of the value
- you must assign a value to the constant on the declaration
- you cannot redefine or mutate a constant (shadowing doesn't work, can't use
mut
keyword, can't reassign)
This ends the article.
I hope you enjoyed it and learned something new. If you have any questions, feel free to leave a comment below.
If you prefer a video version
ALL the links here: https://francescociulla.com
Posted on January 16, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.