Rust LogoUsing Structs to Structure Related Data

In programming, structs (short for structures) are custom data types that allow you to package together several related values into a single, meaningful group. Instead of having individual variables that are conceptually linked (e.g., `user_name`, `user_email`, `user_age`), a struct enables you to define a single `User` type that encapsulates all these pieces of information. This significantly improves code clarity, organization, and maintainability.

Why Use Structs?
* Clarity and Readability: Structs make your code easier to understand by grouping logically related data. For instance, a `Product` struct with fields like `id`, `name`, `price`, and `description` clearly indicates that these attributes belong to a single product.
* Organization and Maintainability: When you need to add a new piece of information to an entity, you only modify the struct definition, rather than tracking down and updating numerous separate variables.
* Type Safety: Structs allow the compiler to ensure that you're always working with a complete and correctly typed set of data for a specific entity, reducing common programming errors.
* Abstraction: They help abstract away the low-level details of how data is stored, allowing you to interact with higher-level concepts (e.g., a 'product object' rather than individual strings and numbers).
* Clean Function Signatures: Instead of passing many individual parameters to a function, you can pass a single struct instance, making function signatures much cleaner and easier to read.

How Structs Work in Rust:
Rust's structs are powerful and integrate well with its ownership and borrowing system.
* Definition: You define a struct using the `struct` keyword, followed by its name (typically `PascalCase`), and then curly braces containing its fields. Each field has a name and a type.
```rust
struct Point {
x: i32,
y: i32,
}
```
* Instantiation: To create an instance of a struct, you provide concrete values for each of its fields.
```rust
let origin = Point { x: 0, y: 0 };
```
* Field Access: You can access individual fields using dot notation (e.g., `origin.x`).
* Methods: Rust structs can have methods (functions associated with the struct instance) and associated functions (functions associated with the struct type itself, often used as constructors). These are defined in an `impl` block.
```rust
impl Point {
fn distance_from_origin(&self) -> f64 {
((self.x as f64).powi(2) + (self.y as f64).powi(2)).sqrt()
}
}
```
* Tuple Structs: These are like tuples but have a name. They are useful when you want to give a tuple a distinct type but don't need named fields.
```rust
struct Color(i32, i32, i32);
let red = Color(255, 0, 0);
println!("Red: ({}, {}, {})", red.0, red.1, red.2);
```
* Unit-Like Structs: These are structs that have no fields. They are often used as markers or when you need to implement a trait on a type but don’t have any data you want to store in the type itself.
```rust
struct Quit;
let msg = Quit;
```

Structs are fundamental building blocks in Rust for creating complex, meaningful data types, enabling you to write more organized, robust, and understandable code.

Example Code

```rust
// Define a struct named 'User'
// It groups together a user's name, email, sign-in count, and active status.
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

// Structs can also have methods defined in an 'impl' block.
// Methods are functions associated with the struct instance.
// Associated functions are functions associated with the struct type itself (like static methods).
impl User {
    // An associated function (constructor-like) to easily create new User instances.
    fn new(username: String, email: String) -> User {
        User {
            username, // Field init shorthand: same as username: username
            email,    // Field init shorthand: same as email: email
            active: true,
            sign_in_count: 1,
        }
    }

    // A method that takes a mutable reference to self (`&mut self`)
    // This allows the method to modify the struct instance.
    fn increment_sign_in(&mut self) {
        self.sign_in_count += 1;
        println!("User '{}' signed in. Total sign-ins: {}", self.username, self.sign_in_count);
    }

    // A method that takes an immutable reference to self (`&self`)
    // This allows reading struct fields but not modifying them.
    fn get_email(&self) -> &str {
        &self.email
    }

    // Another method for demonstration
    fn is_active(&self) -> bool {
        self.active
    }
}

fn main() {
    // 1. Create an instance of the User struct directly
    let mut user1 = User {
        email: String::from("alice@example.com"),
        username: String::from("alice123"),
        active: true,
        sign_in_count: 1,
    };

    // Access fields using dot notation
    println!("User 1: {} (Email: {})", user1.username, user1.email);

    // Modify a field (requires the struct instance to be mutable, i.e., `let mut`)
    user1.email = String::from("alice.updated@example.com");
    println!("User 1 updated email: {}", user1.email);

    // Call a method on the struct instance
    user1.increment_sign_in();

    // 2. Create another instance using the 'new' associated function
    let mut user2 = User::new(
        String::from("bob456"),
        String::from("bob@example.com"),
    );
    println!("User 2: {} (Email: {}, Active: {})", user2.username, user2.get_email(), user2.is_active());

    // Call methods on user2
    user2.increment_sign_in();
    user2.increment_sign_in(); // Call again to show count increasing

    // Demonstrate tuple structs: named tuples
    struct Color(i32, i32, i32); // A tuple struct for RGB values
    let black = Color(0, 0, 0);
    println!("Black RGB: ({}, {}, {})", black.0, black.1, black.2);

    // Demonstrate unit-like structs: structs without any fields
    struct ApplicationStatus;
    let _app_running = ApplicationStatus;
    // Unit-like structs don't have fields to access. They are useful as markers
    // or when you need to implement a trait on a type but don't need to store any data.
    println!("Application status instance created.");
}
```