struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}
 
// Instantiate an instance
let user1 = User {
	active: true,
	username: String::from("someusername123"),
	email: String::from("someone@example.com"),
	sign_in_count: 1,
};
 
let mut user2 = User {
	active: true,
	username: String::from("someusername123"),
	email: String::from("someone@example.com"),
	sign_in_count: 1,
};
 
// Instance must be mutable
user1.email = String::from("anotheremail@example.com");

The entire instance must be mutable. Rust doesnโ€™t allow us to mark only certain fields as mutable.

Keys can be omitted like in JS:

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

Struct update syntax:

let user2 = User {
	email: String::from("another@example.com"),
	..user1 // must be last
};

If range operator .. would include movable data (strings for example), we would โ€œmoveโ€ user1 and could no longer use it.

In this example, we can no longer use user1 as a whole after creating user2 because the String in the username field of user1 was moved into user2. If we had given user2 new String values for both email and username, and thus only used the active and sign_in_count values from user1, then user1 would still be valid after creating user2. Both active and sign_in_count are types that implement the Copy trait, so the behavior we discussed in the โ€œStack-Only Data: Copyโ€ section would apply.

Tuple structs

Structs that look similar to tuples. Tuple structs have the added meaning the struct name provides but donโ€™t have names associated with their fields.

Tuple structs are useful when you want to give the whole tuple a name and make the tuple a different type from other tuples, and when naming each field as in a regular struct would be verbose or redundant.

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

Unit-like structs (without fields)

Unit-like structs can be useful when you need to implement a trait on some type but donโ€™t have any data that you want to store in the type itself.

struct AlwaysEqual;
 
fn main() {
    let subject = AlwaysEqual;
}

These are called unit-like structs because they behave similarly to ()


Most of the time you want to use types that will imply the ownership of a struct data (String instead of string slice type &str), so that data is valid for as long as the entire struct is valid.

Struct can store references to data as well owned by something else, but you would need to use lifetimes.


Methods

struct Rectangle {
    width: u32,
    height: u32,
}
 
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}
 
let rect1 = Rectangle {
	width: 30,
	height: 50,
};
 
rect1.area()

Methods defined within the implementation block.

&self is short for self: &Self.

Within an impl block, the type Self is an alias for the type that the impl block is for.

Methods can take ownership of self (self), borrow self immutably (&self) or borrow self mutably (&mut self), just as they can any other parameter.

we can choose to give a method the same name as one of the structโ€™s fields. When calling with parentheses, Rust will call a method, when using w/o parentheses, Rust will return a fieldโ€™s value.

Rust has automatic referencing and dereferencing. when you call a method with object.something(), Rust automatically adds in &, &mut, or * so object matches the signature of the method. In other words, the following are the same:

p1.distance(&p2);
(&p1).distance(&p2);

This automatic referencing behavior works because methods have a clear receiverโ€”the type of self. Given the receiver and name of a method, Rust can figure out definitively whether the method is reading (&self), mutating (&mut self), or consuming (self). The fact that Rust makes borrowing implicit for method receivers is a big part of making ownership ergonomic in practice.

All functions defined within an impl block are called associated functions because theyโ€™re associated with the type named after the impl.

e can define associated functions that donโ€™t have self as their first parameter (and thus are not methods) because they donโ€™t need an instance of the type to work with, e.g. String::new or String::from.

Each struct is allowed to have multiple impl blocks.