Rust Essential Course – Lesson 7

Lesson 7: Structs and Enums: Basic Data Structures

Introduction

So far, you’ve worked with individual values and references. Now it’s time to group related data together and model real-world concepts in your programs. Rust gives you two powerful tools for this: structs and enums.

  • A struct lets you bundle multiple named fields into one type — perfect for representing objects like a user, a point, or a book.
  • An enum lets you define a type that can be one of several variants — perfect for representing states, options, or categories.

Declaring and Using Structs

A struct is defined with the struct keyword followed by named fields and their types.

struct Point {
    x: f64,
    y: f64,
}

fn main() {
    let p = Point { x: 3.0, y: 4.5 };
    println!("Point is at ({}, {})", p.x, p.y);
}
  • Fields are accessed using dot notation: p.x, p.y.
  • To modify fields, the instance must be declared mut.
fn main() {
    let mut p = Point { x: 1.0, y: 2.0 };
    p.x = 10.0;
    println!("Updated x: {}", p.x);
}

Structs with Functions: impl Blocks

You can associate functions and methods directly with a struct using an impl block. Methods that take &self as their first parameter can be called on an instance.

struct Rectangle {
    width: f64,
    height: f64,
}

impl Rectangle {
    // Associated function (constructor)
    fn new(width: f64, height: f64) -> Rectangle {
        Rectangle { width, height }
    }

    // Method: takes a reference to self
    fn area(&self) -> f64 {
        self.width * self.height
    }

    fn is_square(&self) -> bool {
        self.width == self.height
    }
}

fn main() {
    let rect = Rectangle::new(5.0, 3.0);
    println!("Area: {}", rect.area());
    println!("Is square? {}", rect.is_square());
}
  • Rectangle::new() is an associated function (called on the type, not an instance — similar to a constructor).
  • rect.area() is a method (called on an instance via &self).

Declaring and Using Enums

An enum defines a type that can take on one of a fixed set of values, called variants.

enum Direction {
    North,
    South,
    East,
    West,
}

fn main() {
    let heading = Direction::North;

    match heading {
        Direction::North => println!("Heading North!"),
        Direction::South => println!("Heading South!"),
        Direction::East  => println!("Heading East!"),
        Direction::West  => println!("Heading West!"),
    }
}
  • Enum variants are namespaced: Direction::North.
  • The match expression is the standard way to handle enum values — and Rust requires you to cover all variants (exhaustive matching).

Enums with Data

Rust enums can carry data inside their variants, making them far more powerful than enums in many other languages.

enum Shape {
    Circle(f64),             // radius
    Rectangle(f64, f64),     // width, height
    Triangle(f64, f64, f64), // three sides
}

fn area(shape: &Shape) -> f64 {
    match shape {
        Shape::Circle(r)         => std::f64::consts::PI * r * r,
        Shape::Rectangle(w, h)   => w * h,
        Shape::Triangle(a, b, c) => {
            let s = (a + b + c) / 2.0;
            (s * (s - a) * (s - b) * (s - c)).sqrt()
        }
    }
}

fn main() {
    let c = Shape::Circle(5.0);
    let r = Shape::Rectangle(4.0, 6.0);
    println!("Circle area: {:.2}", area(&c));
    println!("Rectangle area: {:.2}", area(&r));
}

Pattern Matching with match

match is Rust’s powerful control flow construct for pattern matching. It works like a switch statement but is much more expressive.

fn describe_number(n: i32) {
    match n {
        0       => println!("Zero"),
        1..=9   => println!("Single digit"),
        10..=99 => println!("Two digits"),
        _       => println!("Large number"), // _ is a catch-all
    }
}

fn main() {
    describe_number(0);
    describe_number(7);
    describe_number(42);
    describe_number(1000);
}
  • 1..=9 is an inclusive range pattern.
  • _ is the wildcard — it matches anything not covered above.
  • Every match arm ends with => followed by the result expression.

Interactive Task 1: Model a Student

Create a struct called Student with fields name (String), age (u32), and grade (f64). Add an impl block with:

  • A new() associated function to create a student.
  • A is_passing() method that returns true if the grade is 50.0 or above.
struct Student {
    // Your fields here
}

impl Student {
    // Your new() and is_passing() here
}

fn main() {
    let s = Student::new(String::from("Alice"), 20, 72.5);
    println!("Passing: {}", s.is_passing());
}

Try here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=4abf80431f82adee07b83ff9c5562fce

Interactive Task 2: Traffic Light Enum

Define an enum TrafficLight with variants Red, Yellow, and Green. Write a function action that takes a TrafficLight and prints what a driver should do.

enum TrafficLight {
    // Your variants here
}

fn action(light: TrafficLight) {
    // Use match here
}

fn main() {
    action(TrafficLight::Red);
    action(TrafficLight::Green);
    action(TrafficLight::Yellow);
}

Try here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=7a705775ee52aadf673f43bc39337956

Mini-Challenge

Define an enum Coin with variants Penny, Nickel, Dime, and Quarter. Write a function value_in_cents that returns the integer value of each coin using match. Then write a main that prints the value of each coin type.

  • Call your function from main and print each result.
enum Coin {
    // Your code here
}

fn value_in_cents(coin: Coin) -> u32 {
    // Your match here
}

fn main() {
    // Test all four coins
}

Solution: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=37cf708f1f966d0157798845ff094a68

Recap

  • Structs group named fields into a single custom type.
  • impl blocks let you attach methods and associated functions to structs.
  • Enums define a type with a fixed set of variants — which can optionally carry data.
  • match is Rust’s go-to for pattern matching: exhaustive, expressive, and safe.
  • Together, structs and enums form the foundation of idiomatic Rust data modeling.

Questions about structs or enums? Drop a comment!

Leave a Comment

Your email address will not be published. Required fields are marked *