DEV Community

Cover image for [Rust Guide] 3.1. Variables and Mutability
SomeB1oody
SomeB1oody

Posted on

[Rust Guide] 3.1. Variables and Mutability

3.1.0. Before We Begin

Welcome to Chapter 3 of this Rust self-study series. It has 6 sections:

  • Variables and Mutability (this article)
  • Data Types: Scalar Types
  • Data Types: Compound Types
  • Functions and Comments
  • Control Flow: if else
  • Control Flow: Loops

Through the guessing game in Chapter 2 (beginners who have not read it are strongly encouraged to take a look), you should now have learned the basic Rust syntax. In Chapter 3, we will go one level deeper and learn the general programming concepts in Rust.

If you find this helpful, please like, bookmark, and follow. To keep learning along, follow this series.

3.1.1. Declaring Mutable and Immutable Variables

  • Use the let keyword to declare a variable.

  • By default, variables are immutable. Here is an incorrect example; the error is shown in the comment:

fn main(){
    let machine = 6657;
    machine = 0721; // Error: cannot assign twice to immutable variable
    println!("machine is {}", machine);
}
Enter fullscreen mode Exit fullscreen mode
  • You must add mut after let to declare a mutable variable. Here is a successful example; the output is shown in the comment:
fn main(){
    let mut machine = 6657;
    machine = 721;
    println!("machine is {}", machine); // Output: machine is 721
}
Enter fullscreen mode Exit fullscreen mode

3.1.2. Variables and Constants

Many people who are just starting to learn Rust get confused about the difference between immutable variables and constants.
Constants are immutable after they are bound to a value, but they differ from immutable variables in several important ways:

  • Constants cannot use mut; once declared, they are immutable.
  • Constants must be declared with the const keyword, and their type must be explicitly annotated; immutable variables do not have to be.
  • Constants can be declared in any scope, including the global scope.
  • Constants can only be bound to constant expressions; they cannot be bound to the result of a function call or to values that can only be computed at runtime.
  • During program execution, a constant remains valid for the entire scope in which it is declared.
  • Naming convention: Rust constants use all-uppercase letters, with underscores between words, for example: MAX_POINTS.

Here is an example of a constant declaration:

const WJQ: i32 = 66570721;
fn main(){
    const WJQ_MACHINE: u32 = 6_657;
    let mut machine = 6657;
    machine = 721;
    println!("machine is {}", machine); // Output: machine is 721
    println!("WJQ is {}", WJQ); // Output: WJQ is 66570721
    println!("WJQ_MACHINE is {}", WJQ_MACHINE); // Output: WJQ_MACHINE is 6657
}
Enter fullscreen mode Exit fullscreen mode

i32 and u32 are the types. Rust allows underscores to improve readability. In this example, 6_657 could also be written as 6657.
This constant can be declared globally, inside main, or in any other scope.

3.1.3. Shadowing

In the guessing game from earlier, we already briefly mentioned that Rust allows a new variable with the same name to shadow the original one. This is called shadowing (when a name is redefined in the current scope, it hides a variable, function, or type with the same name from an outer scope). Each time a name is shadowed, the original variable's value and type are replaced by the new variable. This lets you reuse the same variable name without declaring a brand-new one.

Here is an example:

fn main(){
let a = 1;
println!("{}", a);
let a = "one";
println!("{}", a);
}
Enter fullscreen mode Exit fullscreen mode

This program does not error, and it prints:

1
one
Enter fullscreen mode Exit fullscreen mode

When the program reaches the second line, a is bound to 1, so it prints 1. On the fourth line, the program notices that a is being reused, so it discards the original value 1 and binds a to "one", which is why the next line prints one. This is shadowing.

Note that shadowing and making a variable mutable are different:

  • In shadowing, the new variable declared with let is still immutable.
  • In shadowing, the type of the newly declared variable with the same name can be different from the previous one.
fn main(){
    let machine = "wjq";
    let machine = 6657;
    println!("{}", machine);
}
Enter fullscreen mode Exit fullscreen mode

The program above uses shadowing and will not error. The second let machine = 6657; declares a brand-new variable, which has nothing to do with the previous machine.

fn main(){
    let mut machine = "wjq";
    machine = 6657;
    println!("{}", machine); // Error: expected `&str`, found integer
}
Enter fullscreen mode Exit fullscreen mode

The program above uses a mutable variable. Rust is a strongly typed language, and a variable's type is determined when it is first declared. The assignment machine = 6657 tries to assign an integer to a string-typed variable, so the types do not match and the compiler reports an error: expected &str, found integer.

Top comments (0)