3.1.0. Before the Main Content
Welcome to Chapter 3 of Rust self-study. There are 6 sections in total:
- 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 small game in Chapter 2 (beginners who havenโt read it are strongly recommended to do so), you should already have learned the basic Rust syntax. In Chapter 3, we will go one level deeper and understand general programming concepts in Rust.
If you like it, please like, bookmark, and follow
3.1.1. Declaring Mutable/Immutable Variables
Variables are declared using the
letkeywordBy default, variables are immutable. The following is an incorrect example, with the error shown in the comment:
fn main(){
let machine = 6657;
machine = 0721; // Error: cannot assign twice to immutable variable
println!("machine is {}", machine);
}
- Add
mutafterletto declare a mutable variable. The following is a correct example:
fn main(){
let mut machine = 6657;
machine = 721;
println!("machine is {}", machine);// Output: machine is 721
}
3.1.2 Variables and Constants
Many people who start learning Rust cannot clearly distinguish between immutable variables and constants (constant). Although constants are also immutable once assigned, they differ significantly from immutable variables:
- Constants cannot use
mut; once declared, they are immutable. - Constants must be declared using
const, and their type must be explicitly specified; immutable variables do not require explicit type annotation. - Constants can be declared in any scope, including the global scope.
- Constants can only be bound to constant expressions and cannot be bound to the result of a function call or values that can only be computed at runtime.
- During program execution, constants are always valid within their declared scope.
- Naming convention: constants in Rust use all uppercase letters, with underscores separating words, e.g.,
MAX_POINTS.
Example of 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
}
Here, i32 and u32 are types. Rust allows underscores to improve readability. In this example, 6_657 can also be written as 6657. This constant can be declared globally, inside main, or in other scopes.
3.1.3 Shadowing
As briefly mentioned in the previous game example, Rust allows using a new variable with the same name to hide the previous one. This is called shadowing (when a variable, function, or type name is redefined in the current scope, it hides the one with the same name from an outer scope). Each time shadowing occurs, the old value and type are replaced.
Example:
fn main(){
let a = 1;
println!("{}",a);
let a = "one";
println!("{}",a);
}
This does not produce an error, and the output is:
1
one
When the program executes line 2, a is 1. At line 4, a is reused, so the old value is discarded and replaced with "one".
Important differences between shadowing and mutability:
- In shadowing, the new variable declared with
letis still immutable - In shadowing, the type of the new variable can be different from the previous one
fn main(){
let machine = "wjq";
let machine = 6657;
println!("{}",machine);
}
This works because of shadowing.
fn main(){
let mut machine = "wjq";
machine = 6657;
println!("{}",machine);// Error: expected `&str`, found integer
}
This fails because Rust is strongly typed, and the variable type is fixed at first declaration.
Top comments (0)