Variables and Mutability
## Learning Objectives
- Declare and initialize variables
- Understand mutability in Rust
- Work with Rust data types
- Master type inference
- Use type annotations
## Variables
### Declaration
```rust
let x = 5; // Immutable variable
let mut y = 10; // Mutable variable
```
### Naming Rules
- Start with letter or underscore
- Can contain letters, digits, underscores
- Case-sensitive
- Cannot use reserved words
```rust
let age = 25; // Valid
let _count = 0; // Valid (suppresses warning)
let total_price = 99.99; // Valid
// let 2ndPlace = 1; // Invalid: starts with number
// let let = 5; // Invalid: reserved word
```
## Mutability
### Immutable by Default
```rust
let x = 5;
x = 6; // Error: cannot assign twice to immutable variable
```
### Making Variables Mutable
```rust
let mut x = 5;
x = 6; // OK: x is mutable
println!("{}", x); // Prints: 6
```
### Why Immutability?
- Prevents bugs from accidental modification
- Enables compiler optimizations
- Makes code easier to reason about
## Data Types
### Scalar Types
#### Integer Types
| Type | Size | Signed |
|------|------|--------|
| i8 | 8-bit | Yes |
| i16 | 16-bit | Yes |
| i32 | 32-bit | Yes |
| i64 | 64-bit | Yes |
| i128 | 128-bit | Yes |
| isize | pointer size | Yes |
| u8 | 8-bit | No |
| u16 | 16-bit | No |
| u32 | 32-bit | No |
| u64 | 64-bit | No |
| u128 | 128-bit | No |
| usize | pointer size | No |
```rust
let a: i32 = 100; // Decimal
let b: i32 = 0x64; // Hexadecimal
let c: i32 = 0o144; // Octal
let d: i32 = 0b1100100; // Binary
let e: i32 = b'C'; // Byte literal
let large: i64 = 9_000_000_000; // Underscores for readability
```
#### Floating-Point Types
```rust
let f1: f64 = 3.14; // 64-bit, default
let f2: f32 = 3.14; // 32-bit
println!("{}", std::f64::consts::PI); // Math constant
```
#### Boolean Type
```rust
let is_active = true;
let is_valid: bool = false;
```
#### Character Type
```rust
let c1 = 'a'; // ASCII character
let c2 = 'Z';
let c3 = '\u{1F600}'; // Unicode emoji
```
### Compound Types
#### Tuple
```rust
let tup: (i32, f64, u8) = (500, 6.4, 1);
// or
let tup = (500, 6.4, 1);
let (x, y, z) = tup; // Destructuring
println!("{} {} {}", x, y, z);
let first = tup.0; // Access by index
let second = tup.1;
```
#### Array
```rust
let arr: [i32; 5] = [1, 2, 3, 4, 5];
// or
let arr = [1, 2, 3, 4, 5];
let first = arr[0]; // Access by index
let len = arr.len(); // Array length
// Array with same value
let zeros = [0; 5]; // [0, 0, 0, 0, 0]
```
### Type Inference
```rust
let x = 5; // Compiler infers i32
let y = 3.14; // Compiler infers f64
let name = "Alice"; // Compiler infers &str
```
### Type Annotations
```rust
let x: i32 = 5;
let y: f64 = 3.14;
let name: &str = "Alice";
let arr: [i32; 3] = [1, 2, 3];
```
## Constants
### const vs let
```rust
let x = 5; // Immutable variable (compiler enforces)
const MAX_SIZE: i32 = 100; // Compile-time constant
const PI: f64 = 3.14159265359; // Type annotation required
const MESSAGE: &str = "Hello"; // String slice
```
### Differences
| Feature | let | const |
|---------|-----|-------|
| Mutability | With `mut` | Never |
| Type annotation | Optional | Required |
| Evaluation | Runtime | Compile-time |
| Scope | Block | Global |
## Type Conversion
### as Operator
```rust
let x: i32 = 5;
let y: f64 = x as f64; // i32 to f64
let z: f64 = 3.99;
let w: i32 = z as i32; // Truncates to 3
let c: char = 65 as char; // Number to character
let n: u8 = b'A'; // Byte literal
```
### Parse String to Number
```rust
let s = "42";
let n: i32 = s.parse().unwrap(); // String to i32
let n: Result = s.parse();
let s = "3.14";
let f: f64 = s.parse().unwrap(); // String to f64
```
### Number to String
```rust
let n = 42;
let s = n.to_string(); // i32 to String
let s = format!("{}", n); // Using format macro
let f = 3.14;
let s = f.to_string();
```
## Variables Scope
```rust
fn main() {
let x = 10;
{
let y = 20; // Inner scope
println!("{} {}", x, y); // OK: both visible
}
println!("{}", x); // OK: x visible
// println!("{}", y); // Error: y not in scope
}
```
## Shadowing
```rust
let x = 5;
let x = x + 1; // Shadows previous x
let x = x * 2; // Shadows again
println!("{}", x); // Prints: 12
// Shadowing with different type
let spaces = " ";
let spaces = spaces.len(); // Now i32
```
## Summary
- Variables are immutable by default
- Use `mut` for mutable variables
- Scalar types: integers, floats, booleans, characters
- Compound types: tuples, arrays
- Type inference reduces verbosity
- Constants use `const` keyword (compile-time)
- Use `as` for type casting
- Shadowing allows re-declaring variables
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →