Thuta Learning
Rust
IntermediateProgrammingbeginner

Structs and impl Blocks

What you'll walk away with

  • Explain the core ideas behind Structs and impl Blocks
  • Run the sample Rust code and verify its output
  • Apply the technique correctly to the Tutorial Platform and production scenarios

Build the mental model

A struct is a custom data type that groups related fields together under names — unlike Lesson 4's tuple, each field has a name, so instead of remembering field order you access data with readable syntax like `lesson.title` or `lesson.word_count`; field names protect large data structures from the field-order bugs tuples are prone to. An `impl` block is the syntax that associates functions with a struct type, distinguishing a method (whose first parameter is `&self`, `&mut self`, or `self`) from an associated function (which takes no `self` parameter at all — the constructor pattern behind `String::from`). Methods are called on an existing instance (`lesson.summary()`), while associated functions are called on the type name directly (`Lesson::new(...)`) — similar to the static method concept in Java or C++. Struct plus `impl` serves the same purpose as a class in object-oriented languages, but Rust has no inheritance — sharing behavior across types comes only from traits (Lesson 14) — and that constraint encourages composition-first design. Choosing `self` as `&self` (a read-only borrow), `&mut self` (a mutable borrow), or `self` (taking ownership, consuming the instance) directly re-applies Lessons 6 and 7's ownership and borrowing rules at the struct-method level, a pattern you will keep encountering.

Connect it to a real scenario

Define the content analyzer project's core data structure as `struct LessonStat { title: String, word_count: u32, difficulty: String }` — self-documenting field names make downstream code readable. For the constructor pattern, write an associated function `impl LessonStat { fn new(title: String, word_count: u32) -> Self { ... } }`, auto-classifying difficulty from word_count (using Lesson 5's `if` expression). Write a read-only summary method with `&self`: `fn summary(&self) -> String { format!("{}: {} words", self.title, self.word_count) }`, so it does not consume the instance and the caller can keep using it afterward.

Try the working example

rust
struct LessonStat {
    title: String,
    word_count: u32,
    difficulty: String,
}

impl LessonStat {
    fn new(title: String, word_count: u32) -> Self {
        let difficulty = if word_count < 500 { "basic" } else { "intermediate" };
        LessonStat { title, word_count, difficulty: difficulty.to_string() }
    }

    fn summary(&self) -> String {
        format!("{} ({}): {} words", self.title, self.difficulty, self.word_count)
    }
}

fn main() {
    let lesson = LessonStat::new(String::from("Ownership"), 620);
    println!("{}", lesson.summary());
}
You should see
You print "Ownership (intermediate): 620 words".

5-minute try-it

Create `struct Tag { name: String, uses: u32 }` — write a `Tag::new(name: String) -> Self` associated function (starting uses at 0) and an `increment(&mut self)` method — create a tag, call increment twice, and print its uses count.

One important caution

Writing a method as `self` (taking ownership) when `&self` would do — calling it consumes the instance, so the caller cannot use it again afterward, leading to a compile error.

Wanting to mutate a field but writing the method signature as `&self` (read-only) — missing that `&mut self` was needed leads straight to "cannot assign to `self.field`".

The Rust Programming Language — Method SyntaxRust

Easy traps

  • Writing a method as `self` (taking ownership) when `&self` would do — calling it consumes the instance, so the caller cannot use it again afterward, leading to a compile error.
  • Wanting to mutate a field but writing the method signature as `&self` (read-only) — missing that `&mut self` was needed leads straight to "cannot assign to `self.field`".
  • Validate sample code in a local or test environment before applying it to a production system.

Exercise

Create `struct Tag { name: String, uses: u32 }` — write a `Tag::new(name: String) -> Self` associated function (starting uses at 0) and an `increment(&mut self)` method — create a tag, call increment twice, and print its uses count.

You'll know it worked when: You print "Ownership (intermediate): 620 words".

Structs and impl Blocks | Thuta Learning