How to define a struct

You define a struct in Rust using the `struct` keyword followed by the name and a list of fields with their types, ending with a semicolon.

You define a struct in Rust using the struct keyword followed by the name and a list of fields with their types, ending with a semicolon. You can create tuple structs, unit structs, or standard named-field structs depending on whether you need named access, positional access, or just a marker type.

Here is how to define and use the three common variations:

// 1. Named-field struct (most common)
struct User {
    username: String,
    active: bool,
    login_count: u32,
}

// 2. Tuple struct (like a named tuple)
struct Color(i32, i32, i32);

// 3. Unit struct (no fields, often used for traits or markers)
struct Empty;

fn main() {
    // Instantiating named-field structs
    let user1 = User {
        username: String::from("alice"),
        active: true,
        login_count: 42,
    };

    // Updating fields using struct update syntax
    let user2 = User {
        username: String::from("bob"),
        ..user1 // Copies remaining fields from user1
    };

    // Instantiating tuple structs
    let red = Color(255, 0, 0);
    
    // Accessing tuple fields by index
    println!("Red value: {}", red.0);

    // Unit structs are instantiated like a value
    let _empty = Empty;
}

When defining structs, remember that fields are private by default unless you explicitly mark them as pub. If you need to create instances easily, you often add a new method or derive the Default trait. For example, adding #[derive(Debug)] to your struct definition allows you to print it using println!("{:?}", user1), which is essential for debugging.

If you need to store data that might be missing, use Option<T> for the field type rather than making the struct itself optional. For complex initialization logic, prefer a constructor function over a public field list to enforce invariants.