Rust by Example: Structs and Methods
Structure your data effectively using Structs. Learn to define custom data types with named fields, tuple structs, and unit structs, and see how to attach methods and associated functions using 'impl' blocks.
Code
// Define a struct
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
// Tuple Structs (named tuples)
struct Color(i32, i32, i32);
// Unit-like Struct (no fields)
struct AlwaysEqual;
// Adding methods using 'impl'
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// Method (takes &self)
fn area(&self) -> u32 {
self.width * self.height
}
// Associated Function (doesn't take self, like a static method)
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
fn main() {
// Creating an instance
let user1 = User {
email: String::from("[email protected]"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
// Struct Update Syntax (copy remaining fields from another instance)
let user2 = User {
email: String::from("[email protected]"),
..user1
};
println!("User 2 active: {}", user2.active);
// Using Tuple Structs
let black = Color(0, 0, 0);
println!("Black R value: {}", black.0);
// Using Methods
let rect1 = Rectangle { width: 30, height: 50 };
println!("Area: {}", rect1.area());
// Using Associated Functions
let sq = Rectangle::square(10);
println!("Square area: {}", sq.area());
}Explanation
Structs (short for structures) are how you create custom data types in Rust. They allow you to name and package together multiple related values that make up a meaningful group. If you're coming from an Object-Oriented background, structs are similar to classes' data fields, but without the attached inheritance hierarchy.
Rust separates data from behavior. You define the shape of the data in the struct block, and then you define the behavior (methods) in a separate impl (implementation) block. This separation keeps data definitions clean and allows you to implement multiple traits for the same struct in different blocks.
There are three types of structs:
- Named-field structs: The standard key-value style. By default, Rust does not guarantee the order of fields in memory (it reorders them to minimize padding). Use
#[repr(C)]if you need C-compatible layout. - Tuple structs: Named tuples useful for creating distinct types from simple values (like
Color(i32, i32, i32)). - Unit-like structs: Structs with no fields (e.g.,
struct AlwaysEqual;). These are Zero-Sized Types (ZSTs) and occupy no actual memory at runtime, but are useful for marker traits.
Code Breakdown
fn area(&self). The &self parameter is short for self: &Self. It means this method borrows the instance. If we used just self, the method would take ownership and consume the instance, which is rarely what you want for a simple calculation.fn square(size: u32). This is an Associated Function because it doesn't take self. It's similar to a static method in Java or C#. We call it using the double colon syntax: Rectangle::square(10)...user1. This is the Struct Update Syntax. It tells Rust to fill in the remaining fields of user2 with the values from user1. It's a concise way to create a new instance based on an old one.
