- Used to encapsulate related properties into one unified data type.
- By convention, the name should follow
PascalCase. - 3 variants,
C-like structs: One or more
,separatedname: value pairsenclosed in{}struct Color { red: u8, green: u8, blue: u8, }Tuple structs: One or more
,separatedvaluesenclosed in()struct Color(u8, u8, u8);Unit structs: A struct with no fields/ members
struct Black;
βοΈ In Rust, data (attributes) and behavior (associated functions and methods) are placed separately. Structs and Enums are used to group related data, and impls and traits are used to add associated and shared behavior to that data.
π‘ In Rust, the term “instantiation” is used to describe the act of creating a concrete instance of a type (struct or enum).
π‘ In Rust, the term “field” is used to describe a named component in a C-like struct & struct-like enum variant, and the term “element” is used to describe an unnamed component in a tuple struct & tuple-like enum variant. The term “member” is used to describe both.
π― More complex examples can be found on Impls and Traits, Lifetimes and Modules sections.
C-like Structs
- Similar to classes (without its methods) in OOP languages.
- Can access fields using the
./ dot notation and the field name.
Definition
struct Color {
red: u8,
green: u8,
blue: u8,
}
Instantiation & Accessing Fields
struct Color {
red: u8,
green: u8,
blue: u8,
}
fn main() {
// 1. Instantiation
let white = Color {
red: 255,
green: 255,
blue: 255,
};
// 2. Instantiation without redundant field names, when using the same variable names
let (red, green, blue) = (0, 0, 0);
let black = Color { red, green, blue };
// 3. Instantiation + copy fields' values from another instance
let red = Color { red: 255, .. black }; // π‘ Copy green and blue from black
let green = Color { green: 255, .. black }; // π‘ Copy red and blue from black
let mut blue = Color { .. black }; // π‘ Copy all fields' values from black
blue.blue = 255;
println!("RGB({}, {}, {})", white.red, white.green, white.blue); // RGB(255, 255, 255)
println!("RGB({}, {}, {})", black.red, black.green, black.blue); // RGB(0, 0, 0)
println!("RGB({}, {}, {})", red.red, red.green, red.blue); // RGB(255, 0, 0)
println!("RGB({}, {}, {})", green.red, green.green, green.blue); // RGB(0, 255, 0)
println!("RGB({}, {}, {})", blue.red, blue.green, blue.blue); // RGB(0, 0, 255)
}
// 4. Instantiation with default values
#[derive(Default)]
struct Person {
name: String,
age: f32,
}
fn main() {
let a = Person::default(); // Instantiation with default values
assert_eq!(a.name, ""); // String default value ""
assert_eq!(a.age, 0.0); // f32 default value 0.0
}
π‘ In Rust, the #[derive()] attribute is used to automatically generate an implementation of certain traits for a custom data structure (struct and enum), instead of you writing them by hand. The std::default::Default trait allows us to create a new instance of a type with the Type::default() method.
π― 5. We can also use a constructor function inside an impl block to initialize a struct.
Destructuring
struct Person {
name: String,
company_name: String,
}
fn get_steve() -> Person {
Person {
name: "Steve Jobs".to_string(),
company_name: "Apple".to_string(),
}
}
fn main() {
let steve = Person {
name: "Steve Jobs".to_string(),
company_name: "Apple".to_string(),
};
let Person {name: a, company_name: b} = steve; // 1. Destructuring fields' values to a and b
println!("{a} {b}"); // Steve Jobs Apple
let Person {company_name: c, .. } = get_steve(); // 2. Destructuring only selected fields' values; directly from the function call
println!("{c}"); // Apple
}
// π― let Person {name: ref a, company_name: ref b} = steve; // add ref keyword, to pass a field's value as a reference
Tuple Structs
- Looks like a named tuples.
- Can access fields using the
./ dot notation and the index number of the field, like on tuples. - βοΈ When a tuple struct has only one element, we call it newtype pattern. Because it helps to create a new type.
Definition
struct Color(u8, u8, u8);
struct Department(String);
Instantiation & Accessing Elements
struct Color(u8, u8, u8);
struct Department(String);
fn main() {
let white = Color(255, 255, 255);
println!("RGB({}, {}, {})", white.0, white.1, white.2); // RGB(255, 255, 255)
let eng_department = Department("Engineering".to_string());
println!("{}", eng_department.0); // Engineering
}
Destructuring
struct Color(u8, u8, u8);
struct Department(String);
fn get_department() -> Department {
Department("Engineering".to_string())
}
fn main() {
let white = Color(255, 255, 255);
let Color(red, green, blue) = white; // π‘ let Color(red, blue, .. ) = white; // Destructuring only selected field's value
println!("RGB({}, {}, {})", red, green, blue); // RGB(255, 255, 255)
let Department(name) = get_department();
println!("{}", name); // Engineering
}
Unit Structs
- It defines a new type, but it resembles an empty tuple,
() - This is rarely useful on its own. But in combination with other features (such as generics), it can become useful.
Definition & Instantiation
struct Electron;
fn main() {
let x = Electron;
}
π ex: A library may ask you to create a structure that implements a certain trait to handle events. If you donβt have any data you need to store in the structure, you can create a unit-like struct.
Debug Printing and Pretty Debug Printing
In Rust, the #[derive()] attribute is used to automatically generate an implementation of certain traits for a custom data structure (struct and enum), instead of you writing them by hand. The std::fmt::Debug trait allows us to format a value with {:?} or {:#?} in println! and similar macros.
#![allow(unused)] // π‘ skip unused warnings, as we don't read fields in the structs
#[derive(Debug)]
struct Electron;
#[derive(Debug)]
struct Department(String);
#[derive(Debug)]
struct Person {
name: String,
company_name: String,
}
fn main() {
let a = Electron;
println!("{a:?}"); // Electron // π‘{a:#?} prints the same
let b = Department("Engineering".to_string());
println!("{b:?}"); // Department("Engineering")
println!("{b:#?}");
// Department(
// "Engineering",
// )
let c = Person { name: "Steve Jobs".to_string(), company_name: "Apple".to_string() };
println!("{c:?}"); // Person { name: "Steve Jobs", company_name: "Apple" }
println!("{c:#?}");
// Person {
// name: "Steve Jobs",
// company_name: "Apple",
// }
}