Variables and Data Types
Chapter 2: Variables and Data Types š
Now that you can run Rust programs, let's learn about storing and manipulating data! This chapter covers variables, mutability, and Rust's type system.
Variables in Rust
Declaring Variables
In Rust, variables are declared using the let keyword:
fn main() {
let x = 5;
println!("The value of x is: {}", x);
}Immutability by Default
Key Concept: Variables in Rust are immutable by default!
fn main() {
let x = 5;
println!("The value of x is: {}", x);
// This would cause a compile error!
// x = 6; // ERROR: cannot assign twice to immutable variable
}Making Variables Mutable
Use the mut keyword to make variables mutable:
fn main() {
let mut x = 5;
println!("The value of x is: {}", x);
x = 6; // This is now allowed!
println!("The value of x is: {}", x);
}Data Types
Rust is a statically typed language, meaning all variable types must be known at compile time.
Scalar Types
Integers
| Length | Signed | Unsigned |
|---|---|---|
| 8-bit | i8 | u8 |
| 16-bit | i16 | u16 |
| 32-bit | i32 | u32 |
| 64-bit | i64 | u64 |
| 128-bit | i128 | u128 |
| arch | isize | usize |
fn main() {
let x: i32 = 42; // 32-bit signed integer
let y: u8 = 255; // 8-bit unsigned integer
let z = 100; // i32 by default
println!("x: {}, y: {}, z: {}", x, y, z);
}Floating-Point Numbers
fn main() {
let x = 2.0; // f64 (default)
let y: f32 = 3.0; // f32
println!("x: {}, y: {}", x, y);
}Boolean
fn main() {
let t = true;
let f: bool = false;
println!("t: {}, f: {}", t, f);
}Character
fn main() {
let c = 'z';
let z = 'ā¤';
let heart_eyed_cat = 'š»';
println!("Characters: {}, {}, {}", c, z, heart_eyed_cat);
}Compound Types
Tuples
Group multiple values with different types:
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
// Destructuring
let (x, y, z) = tup;
println!("The value of y is: {}", y);
// Accessing by index
let first = tup.0;
let second = tup.1;
let third = tup.2;
println!("Tuple values: {}, {}, {}", first, second, third);
}Arrays
Fixed-size collections of the same type:
fn main() {
let arr = [1, 2, 3, 4, 5];
let months = ["January", "February", "March", "April", "May"];
// Array with explicit type and size
let numbers: [i32; 5] = [1, 2, 3, 4, 5];
// Initialize array with same value
let zeros = [0; 3]; // [0, 0, 0]
println!("First element: {}", arr[0]);
println!("Array length: {}", arr.len());
}Type Annotations
Sometimes you need to specify types explicitly:
fn main() {
let guess: u32 = "42".parse().expect("Not a number!");
println!("Guess: {}", guess);
}Constants
Constants are always immutable and must have type annotations:
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
fn main() {
println!("Three hours in seconds: {}", THREE_HOURS_IN_SECONDS);
}Shadowing
You can declare a new variable with the same name as a previous variable:
fn main() {
let x = 5;
let x = x + 1; // Shadows the previous x
{
let x = x * 2; // Shadows again, but only in this scope
println!("The value of x in the inner scope is: {}", x); // 12
}
println!("The value of x is: {}", x); // 6
}Shadowing vs Mutability:
fn main() {
// Shadowing allows type changes
let spaces = " ";
let spaces = spaces.len(); // Now it's a number!
// This wouldn't work with mut:
// let mut spaces = " ";
// spaces = spaces.len(); // ERROR: mismatched types
}String Types
String Literals (&str)
fn main() {
let s = "Hello, world!"; // This is a string slice (&str)
println!("{}", s);
}String Objects (String)
fn main() {
let mut s = String::from("Hello");
s.push_str(", world!");
println!("{}", s);
}Practice Exercises
Exercise 1: Temperature Converter
Create a program that converts Celsius to Fahrenheit:
fn main() {
let celsius = 25.0;
// Formula: F = C * 9/5 + 32
// Your code here
}Exercise 2: Tuple Practice
Create a tuple representing a person (name, age, height) and print each field:
fn main() {
// Your code here
}Exercise 3: Array Operations
Create an array of your 5 favorite numbers and calculate their sum:
fn main() {
// Your code here
}Common Mistakes
- Trying to mutate immutable variables
- Array index out of bounds (Rust will panic at runtime)
- Mixing up
Stringand&str - Forgetting type annotations when needed
Memory Layout
Understanding how data is stored:
fn main() {
let x = 5; // Stored on the stack
let s = String::from("hello"); // String data on heap, pointer on stack
let arr = [1, 2, 3]; // Entire array on stack
}What's Next?
In Chapter 3, we'll learn about functions - how to organize and reuse code!
Key Takeaways
- ā
Variables are immutable by default (use
mutfor mutability) - ā Rust has rich type system with scalars and compounds
- ā Type inference is smart, but sometimes annotations are needed
- ā Constants are always immutable and compile-time evaluated
- ā Shadowing allows reusing variable names with different types
- ā Arrays are fixed-size, tuples can hold different types
Ready for Chapter 3? ā Functions