Skip to main content

Rust

Day 6: Variables Continued

Welcome back, fellow Rustaceans 🦀!

Last time, we explored variable declaration in Rust and its different data types. Today, let's dive into the nitty-gritty rules Rust has around variables and how they differ from languages like C.

As we saw before, you declare variables in Rust using the let keyword, followed by the variable name and an optional type annotation. If you try to compile this code, Rust will print the value of 'x' as 5

fn main() {
    let x = 5;      // x is an integer (type inferred)
    println!("Value of x is {}", x);
}

Explicit Mutability

Now, let's try changing the value of 'x' to 6.

fn main() {
    let x = 5;      // x is an integer (type inferred)
    println!("Value of x before change:- {}", x);
    
    x = 6; // update the value of x
    
    println!("Value of x after change:- {}", x);
}

Oops, the code doesn't compile! The compiler complains: "cannot assign twice to immutable variable x".

Immutable? What's That? This is where Rust starts to stand out. By default, all variables in Rust are immutable (read-only). This promotes safety and prevents accidental changes. To make a variable mutable (changeable), you need to explicitly use the mut keyword. Our compiler even suggests this fix: "Consider making this binding mutable: mut x". Let's try that:

fn main() {
    let mut x = 5;      // x is an integer (type inferred)
    println!("Value of x before change:- {}", x);
    
    x = 6; // update the value of x
    
    println!("Value of x after change:- {}", x);
}

At first, marking variables with mut might feel a bit extra, but it's a great way to communicate your intent and help prevent unexpected modifications.

‼️
Rust Hates Uninitialized Variables

Let's take a look at this C code:

// C code example
int x; // Declaring an integer 'x' without initializing
printf("%d\n", x); // Trying to print its value... what's going to happen?

What do you think will be printed? If you're new to programming, you might expect zero. But seasoned C veterans know better – the value of an uninitialized local variable is unpredictable! Even global uninitialized variables, which technically get set to zero, are manually initialized to zero during bss init in startup code. For example https://github.com/inpyjama/stm32-renode/blob/f8ae008a86c02ff2282c7da99399abd72625d2c1/src/startup.c#L37

Accessing unpredictable values... not exactly ideal, right? It's certainly not what you usually intend. Now, let's see how Rust handles the same situation:

fn main() {
    // Rust code attempt
    let x: u32; // Uh oh, no initialization!
    println!("{}", x); // Rust won't let us get away with this
    
}

Boom! The Rust compiler won't even let this code compile. It throws an error: "use of possibly uninitialized variable x". Rust hates uninitialized variables and even suggests we initialize 'x' with zero. Let's try that, and voila – the code works!

Constants and Statics