Definition outside of main function
key: value pair is called field
Struct’s data type is its name in the definition
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,
};
// if the instance is mutable its values can be changed
user1.email = String::from("[email protected] ");
}
fn build_user(email: String, username: String) -> User {
User {
active: true,
username: username,
email: email,
sign_in_count: 1,
}
}
nor need to repeat key: value if the function’s parameters and field’s names are the same
fn build_user(email: String, username: String) -> User {
User {
active: true,
username,
email,
sign_in_count: 1,
}
}
new instance of struct includes values from another instance of the same type and updates the others
fn main() {
let user2 = User {
active: user1.active,
username: user1.username,
email: String::from("[email protected] "),
sign_in_count: user1.sign_in_count,
};
}
it can be further simplified to:
fn main() {
let user2 = User {
email: String::from("[email protected] "),
..user1
};
}
Struct update either moves/copies data depending on data types
In the example above user1 cannot be used anymore as its username changed the ownership due to its String data type
Similar to tuples but the tuple itself has a name
Each tuple struct is its own type
As normal tuples:
no names associated with their fields
they can be destructured
individual values can be accessed with . followed by the index
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
}
similar to unit type () (empty tuple)
struct AlwaysEqual;
fn main() {
let subject = AlwaysEqual;
}
Similar to functions:
Declared with fn
Can take parameters and have return value
Contain code
Defined in the context of struct
Their first parameter is always self which represents the instance of the struct the method is being called on
Methods can take ownership so you can define parameter:
self (takes ownership)
&self (reference borrowing)
&mut self (mutable reference)
Note: parameter &self is equivalent of self: &self
Defined within impl (implementation) block
#[derive(Debug)] // allows to print struct with println macro
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()
);
}
other parameters can be defined after self parameter as in function
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
let rect2 = Rectangle {
width: 10,
height: 40,
};
let rect3 = Rectangle {
width: 60,
height: 45,
};
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
All functions defined within impl block are associated functions
They are associated with the type named after impl
Associated function is not method if parameter self is not present (they do not need an instance to work with)
Often used for constructors returning a new instance of struct
self in the return type is alias for the type that follows impl
Associated function is called with ::
fn main() {
let sq = Rectangle::square(3);
}
impl Rectangle {
fn square(size: u32) -> Self {
Self {
width: size,
height: size,
}
}
}