Variables are fundamental to programming, and Rust offers a unique way of handling them.
Rust emphasizes safety and immutability by default, which means variables are immutable unless explicitly stated otherwise.
This tutorial will cover:
1. Declaring Variables
In Rust, variables are declared using the let keyword. By default, variables are immutable, meaning their value cannot be changed once assigned.
Example: Immutable Variables
fn main() { let x = 10; // Immutable variable println!("x = {}", x); // Uncommenting the next line will cause a compile error // x = 20; // Error: cannot assign twice to an immutable variable }
2. Mutable Variables
To create a variable whose value can change, use the mut keyword.
Example: Mutable Variables
fn main() { let mut y = 5; // Mutable variable println!("y = {}", y); y = 10; // Changing the value println!("y = {}", y); }
3. Shadowing Variables
Rust allows shadowing, where a variable with the same name can be declared again, effectively replacing the previous variable. This is different from mutability as the type or value can change in each shadowing.
Example: Variable Shadowing
fn main() { let z = 5; println!("z = {}", z); let z = z + 1; // Shadowing: `z` is redefined println!("z = {}", z); let z = "Rust"; // Shadowing with a different type println!("z = {}", z); }
4. Constants
Constants are similar to variables but have the following restrictions:
- Declared using the const keyword.
- Always immutable (cannot use mut).
- Must have a type annotation.
- Must be initialized with a constant expression.
Example: Constants
fn main() { const PI: f64 = 3.14159; const MAX_SCORE: u32 = 100; println!("PI = {}", PI); println!("Max score = {}", MAX_SCORE); }
5. Data Types in Variables
5.1 Explicit Type Annotations
You can specify the type of a variable explicitly.
fn main() { let a: i32 = 10; // 32-bit signed integer let b: f64 = 3.14; // 64-bit floating point let c: bool = true; // Boolean let d: char = 'R'; // Character println!("a = {}, b = {}, c = {}, d = {}", a, b, c, d); }
5.2 Type Inference
Rust can infer the type of variables based on their assigned value.
fn main() { let x = 42; // Inferred as i32 let y = 2.718; // Inferred as f64 println!("x = {}, y = {}", x, y); }
6. Default Type Inference
Rust assigns default types when no type is specified:
- Integer literals default to i32.
- Floating-point literals default to f64.
Example: Default Types
fn main() { let num = 100; // Defaults to i32 let decimal = 2.5; // Defaults to f64 println!("num = {}, decimal = {}", num, decimal); }
7. Scope and Lifetime
Variables in Rust have a scope that determines where they are accessible. A variable is dropped (deallocated) when it goes out of scope.
Example: Variable Scope
fn main() { { let inner = "This is inner scope"; println!("{}", inner); } // println!("{}", inner); // Error: `inner` is not accessible here }
8. Practical Examples
8.1 Swapping Variables
You can swap the values of two variables using tuple destructuring.
fn main() { let mut a = 10; let mut b = 20; println!("Before swap: a = {}, b = {}", a, b); // Swap values (a, b) = (b, a); println!("After swap: a = {}, b = {}", a, b); }
8.2 Using Shadowing for Transformations
Shadowing is helpful for applying transformations without creating new variable names.
fn main() { let price = 100; // Original price let price = price * 2; // Double the price let price = price - 10; // Apply a discount println!("Final price: {}", price); }
8.3 Working with Mutable Variables
Mutable variables are useful when state needs to change.
fn main() { let mut counter = 0; for _ in 0..5 { counter += 1; // Increment counter println!("Counter: {}", counter); } }
8.4 Combining Constants and Variables
Combine constants and variables for efficient calculations.
fn main() { const TAX_RATE: f64 = 0.15; let price = 100.0; let total = price + (price * TAX_RATE); println!("Total price with tax: {:.2}", total); }
Key Takeaways
- Variables in Rust are immutable by default, promoting safety and predictability.
- Use mut to make variables mutable when necessary.
- Shadowing allows redeclaring variables, enabling transformations and reassignments with flexibility.
- Constants are immutable, require explicit type annotations, and are evaluated at compile time.
Rust’s variable system, with its emphasis on immutability, helps prevent bugs and encourages good programming practices.