Structs are similar to tuples, but it has a name so we don’t need to use the ordering to obtain the values.

To define a struct, we use the keyword struct and name the entire struct.

struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

fn main() {
    let mut user1 = User {
        active: true,
        username: String::from("someusername123"),
        email: String::from("[email protected]"),
        sign_in_count: 1,
    };
		// only mutatable if the struct itself is of the mut type
		user1.email = String::from("[email protected]"); 
}

Notice that if a parameter’s name is same as the field name, we can just pass it in instead of specifying the parameter

fn build_user(email: String, username: String) -> User {
    User {
        active: true,
        username,
        email,
        sign_in_count: 1,
    }
}

Rust also support spread using ...

let user2 = User {
    email: String::from("[email protected]"),
    ..user1
};

by doing so, we are only updating the email and remain everything else the same in user2. This uses = like assignment. Thus, after we did the above operation, user1 might no longer be usable as a whole(some of the values will be moved to user2 if they didn’t implement the Copy trait).

Using Tuple Structs Without Named Fields to Create Different Types

Rust supports tuple structs which can be thought of named tuples but don’t have names associated with their fields.

struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

fn main() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);
}

black and origin values are different types because they’re instances of different structs. A function that takes a parameter of type Color cannot take a Points as an argument.

Unit-Like Structs Without Any Fields

Those are structs that don’t have any fields, which behaved like (). This can be useful when we need to implement a trait on some type but don’t have any data to store. More on this later.

struct AlwaysEqual;

fn main() {
    let subject = AlwaysEqual;
}

Method Syntax

Methods are similar to functions: we declare them with the fn keyword and a name

Defining Methods

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!(
        "The area of the rectangle is {} square pixels.",
        rect1.area()
    );
}

To define a function within the context of a struct, we start an impl block for that struct. Everything within this impl block will be associated with the struct. Then we can put function within the impl brackets and change the first parameter to be &self in the signature and everywhere within the body. If this method is gonna mutate the data, we need to change the parameter to &mut self