You define a struct in Rust using the struct keyword followed by the name and a list of fields with their types, ending with a semicolon. You can create tuple structs, unit structs, or standard named-field structs depending on whether you need named access, positional access, or just a marker type.
Here is how to define and use the three common variations:
// 1. Named-field struct (most common)
struct User {
username: String,
active: bool,
login_count: u32,
}
// 2. Tuple struct (like a named tuple)
struct Color(i32, i32, i32);
// 3. Unit struct (no fields, often used for traits or markers)
struct Empty;
fn main() {
// Instantiating named-field structs
let user1 = User {
username: String::from("alice"),
active: true,
login_count: 42,
};
// Updating fields using struct update syntax
let user2 = User {
username: String::from("bob"),
..user1 // Copies remaining fields from user1
};
// Instantiating tuple structs
let red = Color(255, 0, 0);
// Accessing tuple fields by index
println!("Red value: {}", red.0);
// Unit structs are instantiated like a value
let _empty = Empty;
}
When defining structs, remember that fields are private by default unless you explicitly mark them as pub. If you need to create instances easily, you often add a new method or derive the Default trait. For example, adding #[derive(Debug)] to your struct definition allows you to print it using println!("{:?}", user1), which is essential for debugging.
If you need to store data that might be missing, use Option<T> for the field type rather than making the struct itself optional. For complex initialization logic, prefer a constructor function over a public field list to enforce invariants.