Beginner Level

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

  1. Trying to mutate immutable variables
  2. Array index out of bounds (Rust will panic at runtime)
  3. Mixing up String and &str
  4. 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 mut for 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

šŸ¦€ Rust Programming Tutorial

Learn from Zero to Advanced

Built with Next.js and Tailwind CSS • Open Source