# Rust Structs: From Scattered Variables to Organized, Method-Powered Types

I was staring at a function signature that looked like this:
 
```rust
fn area(width: u32, height: u32) -> u32 {
    width * height
}
```
 
And something felt off. Not wrong — it works. But it *smells*. Width and height clearly belong together. They're describing the same thing. Why are they just floating around as separate arguments like two strangers at a party who definitely know each other?
 
That's when structs clicked for me.
 
---
 
## What Is a Struct, Really?
 
Think of a struct like a form. A user registration form has fields: name, email, whether the account is active. Those fields don't make sense in isolation — they belong together under the concept of "a user." A struct is how you express that grouping in Rust.
 
Where tuples let you group things by position (`(30, 50)` — wait, which one is width?), structs let you group things by *name*. That's the core win.
 
Here's the User struct I practiced with:
 
```rust
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}
```
 
Different types, named fields, grouped under one concept. Instantly more readable than a tuple of `(String, String, u64, bool)`.
 
---
 
## Creating and Using Instances
 
Once you have a struct defined, you create *instances* of it. Each instance fills in the actual values for those fields:
 
```rust
let mut user1 = User {
    email: String::from("padfoot@gmail.com"),
    username: String::from("padfoot"),
    sign_in_count: 1,
    active: true,
};
```
 
Fields don't have to go in declaration order — Rust doesn't care. Access them with dot notation:
 
```rust
let name = user1.username;
```
 
And if you marked the instance `mut`, you can update fields the same way:
 
```rust
user1.username = String::from("0x12md10");
```
 
One important thing I noticed: **mutability is all-or-nothing on the whole struct**. You can't say "only this field is mutable." Either the whole instance is `mut` or none of it is.
 
![All or Nothing](https://media.tenor.com/pVSeBDWeMSsAAAAj/it%27s-all-or-nothing-mate-colin-lawson.gif align="center")
 
---
 
## Build Functions and Field Init Shorthand
 
Often you'll want a constructor-style function to build instances. I wrote one called `build_user`:
 
```rust
fn build_user(email: String, username: String) -> User {
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}
```
 
Notice anything about `email` and `username` in the struct literal? No `email: email` — just `email`. When a function parameter has the same name as the struct field, Rust lets you use the **field init shorthand** and skip the redundant assignment. It's a small thing but it removes a lot of visual noise when you have many fields.
 
---
 
## Struct Update Syntax
 
Another handy feature: creating a new instance from an existing one, only overriding what's different.
 
```rust
let user2 = build_user(String::from("harry@gmail.com"), String::from("Harry"));
 
let user3 = User {
    email: String::from("BruceW@gmail.com"),
    username: String::from("Bruce Wayne"),
    ..user2
};
```
 
The `..user2` says: "for any fields I haven't specified, copy them from `user2`." So `user3` gets `active` and `sign_in_count` from `user2`. Clean.
 
---
 
## Tuple Structs: Named Tuples
 
Sometimes you want a tuple to have a type name — so you can distinguish a `Color` from a `Point` even when they have the same shape.
 
```rust
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
```
 
Both have three `i32`s. But they're different types. A function expecting a `Point` won't accept a `Color`. This is useful when type safety matters more than field names.
 
![they look the same but they're not meme](https://media1.giphy.com/media/v1.Y2lkPTc5MGI3NjExbGtuNWd3Y2ZidXNncWEwMXRkYmd5OGR2d3gwMGJhN3U5YmRodG9mMCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/l36kU80xPf0ojG0Erg/giphy.gif align="center")
 
---
 
## The Rectangle Refactor: Why Structs Actually Matter
 
This is where things got concrete for me. I started with this:
 
```rust
fn main() {
    let width1 = 30;
    let height1 = 50;
    println!("Area: {}", area(width1, height1));
}
 
fn area(width: u32, height: u32) -> u32 {
    width * height
}
```
 
It works. But `width1` and `height1` are just floating variables with no relationship expressed in code. I refactored through tuples first:
 
```rust
let rect = (30, 50);
 
fn area(dimensions: (u32, u32)) -> u32 {
    dimensions.0 * dimensions.1
}
```
 
Better — one variable now. But `dimensions.0` vs `dimensions.1`? Which is width again? The code lost meaning in the process.
 
The final version with a struct is clearly the best:
 
```rust
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}
 
fn main() {
    let rect = Rectangle { width: 30, height: 50 };
    println!("Area: {}", area(&rect));
}
 
fn area(rectangle: &Rectangle) -> u32 {
    rectangle.width * rectangle.height
}
```
 
Named fields. A real type. And notice we're passing `&rect` — a *reference* — because we want to use the rectangle without taking ownership of it. The function just borrows it.
 
---
 
## Printing Structs with `#[derive(Debug)]`
 
When I tried to `println!` a struct, Rust immediately complained:
 
```
`Rectangle` doesn't implement `std::fmt::Display`
```
 
Primitive types know how to display themselves. Custom structs don't — there's no single "right" way to format them. But Rust gives you a shortcut for debug-style printing:
 
1. Add `#[derive(Debug)]` above the struct
2. Use `{:?}` in the format string (or `{:#?}` for pretty-printed multiline output)
```rust
#[derive(Debug)]
struct Rectangle { width: u32, height: u32 }
 
println!("rect: {:?}", rect);   // compact
println!("rect: {:#?}", rect);  // pretty
```
 
`#[derive(Debug)]` tells the compiler to auto-generate a Debug implementation for you. You'll use this constantly while building things out.
 
---
 
## Methods: Attaching Behavior to Your Struct
 
Here's the real power move. That `area` function is intimately tied to `Rectangle` — but it's defined separately, hanging out in the global scope. Methods let you attach it directly to the struct using an `impl` block:
 
```rust
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}
```
 
The first parameter is always `&self` — a reference to the instance the method is called on. Now you call it like:
 
```rust
println!("Area: {}", rect.area());
```
 
Much cleaner. And it makes the relationship explicit: `area` belongs to `Rectangle`.
 
I also wrote a `can_hold` method that takes another rectangle as an argument:
 
```rust
impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}
```
 
Two parameters: `self` (the current instance) and `other` (a borrowed reference to another `Rectangle`). Usage:
 
```rust
if rect.can_hold(&rect1) {
    println!("Current rectangle can hold the other.");
} else {
    println!("Current rectangle cannot hold the other.");
}
```
 
Rust handles the referencing and dereferencing automatically when you call methods — you don't need different syntax for calling a method on a value vs. a pointer like you would in C++.
 
![it just works magic gif](https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExeThocWdlYnRmYzlma3JoM2w5M2M1NDJjeGhoa2tuM2dqaGx2NW85ZiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/MXh5hUhtoUhytIg89s/giphy.gif align="center")
 
---
 
## Associated Functions: The Struct's Static Methods
 
Not everything in an `impl` block needs to be a method. **Associated functions** don't take `self` as a parameter — they're tied to the *type*, not to an *instance*. The most common use is constructors.
 
```rust
impl Rectangle {
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
}
```
 
You call this with `::` syntax, not dot notation:
 
```rust
let rect2 = Rectangle::square(2);
```
 
`String::from(...)` is an associated function. `Vec::new()` is an associated function. Now you know what that `::` means.
 
You can have multiple `impl` blocks for the same struct — Rust allows it. Normally you'd keep everything in one, but it becomes useful with generics and traits (a later chapter).
 
---
 
## Bringing It All Together
 
Here's the final version of the Rectangle code from my practice session:
 
```rust
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}
 
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
 
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}
 
impl Rectangle {
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
}
 
fn main() {
    let rect = Rectangle { width: 30, height: 50 };
    let rect1 = Rectangle { width: 20, height: 30 };
    let rect2 = Rectangle::square(2);
 
    println!("rect2: {:#?}", rect2);
    println!("Area: {} square pixels.", rect.area());
 
    if rect.can_hold(&rect1) {
        println!("rect can hold rect1.");
    } else {
        println!("rect cannot hold rect1.");
    }
}
```
 
From two scattered variables to a named type with behavior — that's the struct journey.
 
---
 
## Key Takeaways
 
- **Structs group related data with named fields**, making your code self-documenting. Prefer them over tuples when fields have distinct meaning.
- **Methods live in `impl` blocks** and always take `&self` (or `&mut self`) as the first parameter. Call them with dot notation.
- **Associated functions** (no `self`) are called with `::` syntax — use them for constructors and type-level utilities like `Rectangle::square(5)`.
---
 
## References
 
- The Rust Book, Chapter 5: [https://doc.rust-lang.org/book/ch05-00-structs.html](https://doc.rust-lang.org/book/ch05-00-structs.html)
