Rust LogoDefining and Instantiating Structs

Structs, short for "structures," are custom data types that let you group together related pieces of information. In Rust, structs are similar to records in other languages, allowing you to create more complex and meaningful data structures than primitive types alone. They are fundamental for organizing data and defining the properties of objects in your programs.

Defining a Struct:
To define a struct, you use the `struct` keyword followed by the struct's name and a set of curly braces `{}` containing its fields. Each field has a name and a data type, separated by a colon.
```rust
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
```
* Struct Naming: Struct names traditionally use `UpperCamelCase`.
* Field Naming: Field names traditionally use `snake_case`.
* Field Types: Each field must have an explicit type. Rust's type inference doesn't apply to struct fields.

Instantiating a Struct:
Once a struct is defined, you can create concrete instances of that struct. Instantiating a struct means creating a specific value of that custom type. You do this by specifying concrete values for each of its fields.
```rust
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
```
* Order of Fields: The order of fields when instantiating doesn't need to match the order in the struct definition.
* Accessing Field Values: You can access individual field values using dot notation: `user1.email`.
* Mutability: By default, struct instances are immutable. To allow changing the values of fields within an instance, you must make the instance mutable using the `mut` keyword:
```rust
let mut user2 = User { /* ... */ };
user2.email = String::from("another@example.com"); // This is allowed
```
* Field Init Shorthand: If a variable name is the same as a struct field name, you can use the field init shorthand syntax.
```rust
let email = String::from("test@example.com");
let username = String::from("testuser");
let user3 = User {
email, // shorthand for email: email
username, // shorthand for username: username
active: true,
sign_in_count: 5,
};
```
* Struct Update Syntax: You can create new instances from existing ones, copying some values and overriding others, using the struct update syntax (`..` followed by an existing instance).
```rust
let user4 = User {
email: String::from("new@example.com"),
..user1 // copies username, active, sign_in_count from user1
};
```
Note that `user1` must be "moved" if its fields are not Copy types (like `String`). This means `user1` might become unusable after the update, as its data is transferred to `user4`. If you need to keep the original struct usable, you would need to manually `clone()` the non-Copy fields that you wish to retain in the original struct, or ensure the fields are `Copy` types.

Example Code

// 1. Defining a Struct
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

// Another struct for demonstration of updates
struct Product {
    name: String,
    price: f64,
    in_stock: bool,
}

fn main() {
    // 2. Instantiating a Struct (Immutable instance)
    let user1 = User {
        email: String::from("alice@example.com"),
        username: String::from("alice_wonder"),
        active: true,
        sign_in_count: 1,
    };

    println!("User 1:");
    println!("  Email: {}", user1.email);
    println!("  Username: {}", user1.username);
    println!("  Active: {}", user1.active);
    println!("  Sign-in Count: {}", user1.sign_in_count);
    println!("");

    // 3. Instantiating a Struct (Mutable instance)
    let mut user2 = User {
        email: String::from("bob@example.com"),
        username: String::from("bob_builder"),
        active: false,
        sign_in_count: 5,
    };

    println!("User 2 (before modification):");
    println!("  Email: {}", user2.email);

    // Modifying a field of a mutable instance
    user2.email = String::from("bob_new_email@example.com");
    user2.active = true;
    user2.sign_in_count += 1;

    println!("User 2 (after modification):");
    println!("  Email: {}", user2.email);
    println!("  Active: {}", user2.active);
    println!("  Sign-in Count: {}", user2.sign_in_count);
    println!("");

    // 4. Field Init Shorthand
    let user_email = String::from("charlie@example.com");
    let user_name = String::from("charlie_chaplin");
    let user3 = User {
        email: user_email,   // 'email' field takes value from 'user_email' variable
        username: user_name, // 'username' field takes value from 'user_name' variable
        active: true,
        sign_in_count: 10,
    };

    println!("User 3 (using field init shorthand):");
    println!("  Email: {}", user3.email);
    println!("  Username: {}", user3.username);
    println!("");

    // 5. Struct Update Syntax
    // Create a new User instance based on user3, but with a different email and count.
    // Note: Since String is not Copy, user3 will be moved and cannot be used after this.
    // If you needed to keep user3, you'd clone relevant fields or change types to be Copy.
    let user4 = User {
        email: String::from("david@example.com"),
        sign_in_count: 2,
        ..user3 // Copies username and active from user3. user3 is moved.
    };

    println!("User 4 (using struct update syntax from user3):");
    println!("  Email: {}", user4.email);
    println!("  Username: {}", user4.username);
    println!("  Active: {}", user4.active);
    println!("  Sign-in Count: {}", user4.sign_in_count);
    println!("");

    // Example with another struct to clarify struct update behavior and cloning
    let product1 = Product {
        name: String::from("Laptop"),
        price: 1200.0,
        in_stock: true,
    };

    // To use product1 after the update with struct update syntax,
    // we must clone its non-Copy fields before moving the rest.
    let product2 = Product {
        name: product1.name.clone(), // Clone 'name' to keep product1.name usable
        price: 1500.0,
        ..product1 // This moves 'in_stock' (which is Copy, so it's fine) and implicitly consumes product1
    };

    println!("Product 2 (updated from product1 with clone):");
    println!("  Name: {}, Price: {}, In Stock: {}", product2.name, product2.price, product2.in_stock);
    // println!("Product 1 (original): {}", product1.name); // This would cause a compile error because product1 was moved
    println!("");
}