Struct
Comprehension
Basic syntax
// type declaration struct User { active: bool, username: String, email: String, sign_in_count: u64, } // instantiation let user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; // access & modify let mut user1 = User { email: String::from("someone@example.com"), username: String::from("someusername123"), active: true, sign_in_count: 1, }; user1.email = String::from("anotheremail@example.com"); // field init shorthand fn build_user(email: String, username: String) -> User { User { email, username, active: true, sign_in_count: 1, } } // struct update let user2 = User { email: String::from("another@example.com"), ..user1 } // user1 is moved here because user1.username (a String) is moved to user2.username // if username is assigned separately when initializing user2, user1 is not movedMethod
A function inside a struct impl is called a method or an associated function; a method can have the same name as a field
Multiple impl blocks can coexist
#[derive(Debug)] struct Rectangle { width: u32, height: u32, } impl Rectangle { fn area(&self) -> u32 { // method self.width * self.height } fn width(&self) -> bool { // a method can have the same name as a field self.width > 0 } fn square(size: u32) -> Rectangle { // &self is optional Rectangle { width: size, height: size, } } } // Multiple impl blocks can coexist impl Rectangle { fn can_hold(&self, other: &Rectangle) -> bool { self.width > other.width && self.height > other.height } } fn main() { let rect1 = Rectangle { width: 30, height: 50, }; println!( "The area of the rectangle is {} square pixels.", rect1.area() ); if rect1.width() { println!("The rectangle has a nonzero width; it is {}", rect1.width); } }
Origin
…
Creating Instances From Other Instances With Struct Update Syntax
It’s often useful to create a new instance of a struct that uses most of an old instance’s values but changes some. You can do this using struct update syntax.
First, Listing 5-6 shows how we create a new User instance in user2 without the update syntax. We set a new value for email but otherwise use the same values from user1 that we created in Listing 5-2.
let user2 = User {
active: user1.active,
username: user1.username,
email: String::from("another@example.com"),
sign_in_count: user1.sign_in_count,
};
Listing 5-6: Creating a new User instance using one of the values from user1
Using struct update syntax, we can achieve the same effect with less code, as shown in Listing 5-7. The syntax .. specifies that the remaining fields not explicitly set should have the same value as the fields in the given instance.
let user2 = User {
email: String::from("another@example.com"),
..user1
};
Listing 5-7: Using struct update syntax to set a new email value for a User instance but use the rest of the values from user1
The code in Listing 5-7 also creates an instance in user2 that has a different value for email but has the same values for the username, active, and sign_in_count fields from user1. The ..user1 must come last to specify that any remaining fields should get their values from the corresponding fields in user1, but we can choose to specify values for as many fields as we want in any order, regardless of the order of the fields in the struct’s definition.
Note that the struct update syntax is like assignment with = because it moves the data, just as we saw in the
“Ways Variables and Data Interact: Move” section
. In this example, we can no longer use user1 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. The types of 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.