Structs are similar to tuples. They allow to hold related data together
Unlike a tuple each element in the Struct is assigned a name
Structs are comparable to the concept of dataclass
present in Python
struct User {
name: String,
username: String,
email: String,
active: bool,
login_count: u32,
}
fn main() {
let mut user1 = User {
active: true,
login_count: 1,
name: String::from("David"),
email: String::from("[email protected]"),
username: String::from("dvdmtw"),
};
println!("Username: {}", user1.username);
user1.username.push_str("12");
println!("Username: {}", user1.username);
}
To mutable even a single field in the struct, the entire struct has to become mutable
Values for fields in the struct can be provided in a order different from the definition order
When using a function to initialize a struct, if the names of the input parameters are the same as the the struct field names then the initialization shorthand
notation can be used
fn build_user(name: String, username: String, email: String) -> User {
User {
name,
username,
email: email,
active: true,
login_count: 1,
}
}
The fields of a struct can be initialized using the values of fields from another struct
let user3 = User {
name: String::from("Jane"),
email: String::from("[email protected]"),
username: String::from("jane123"),
..user2
};
Data Move
- When initializing structs using this method the Compound datatype values are moved to the newly created struct (Ownership - Rule 2)
- In the above example we would no longer be able to use the username field from user2
Struct fields can contain reference type data (Data that is borrowed from another variable) but this requires the use of lifetimes
Tuple Structs
Used to create a struct where the tuple needs to be assigned a name but assigning names to fields are too verbose for the scenario
Instead of field names the datatype of the fields are defined in the struct
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 Structs
Defining and Instantiating Structs - The Rust Programming Language
Methods
Methods are functions that are defined within the context of the struct
The first parameter to a method is always self which points to the current instance
The methods that are tied to a struct need to be defined in a impl
(implementation) block
Multiple impl
blocks can also be created for an struct
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
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
}
}
fn main() {
let rect1 = Rectangle {
width: 10,
height: 30,
};
let rect2 = Rectangle {
width: 5,
height: 40,
};
let area1 = rect1.area();
let area2 = rect2.area();
println!("Area of Rect1: {}", area1);
println!("Area of Rect2: {}", area2);
let can_hold = rect1.can_hold(&rect2);
println!("Can Rect1 hold Rect2?: {}", can_hold);
}
Functions defined inside impl
block without a &self
reference are called associated functions
Associated functions are generally used to create constructors
Associated Functions are called using the ::
syntax with the struct name
impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
fn main() {
let rect = Rectangle::square(10);
println!("Area of Square: {}", rect.area());
}