Skip to content
DocsRust LearningintermediateStructs
Chapter 6 of 19·intermediate·6 min read

Structs

Cấu Trúc

Custom data types with methods

Hover or tap any paragraph to see Vietnamese translation

Defining and Instantiating Structs

A struct is a custom data type that lets you name and package together multiple related values into a meaningful unit. If you are familiar with object-oriented programming, a struct is like the data attributes of a class — but without inheritance.

src/main.rs
1// Define a struct with named fields2struct User {3    active: bool,4    username: String,5    email: String,6    sign_in_count: u64,7}89fn main() {10    // Instantiate a struct — ALL fields must be provided11    let user1 = User {12        active: true,
Info
In Rust, you cannot mark only certain fields as mutable. Mutability applies to the entire struct instance — there is no per-field mutability.

Field Init Shorthand

When a function parameter name and a struct field name are identical, you can use the field init shorthand syntax to avoid repeating the name. This is very common in constructor functions.

src/main.rs
1struct User {2    active: bool,3    username: String,4    email: String,5    sign_in_count: u64,6}78// Without shorthand — repetitive9fn build_user_verbose(email: String, username: String) -> User {10    User {11        active: true,12        username: username, // redundant

Struct Update Syntax

Struct update syntax allows creating a new instance from an old one, overriding some fields while taking the remaining fields from the original instance. The .. syntax must come last and specifies the source for fields not explicitly listed.

src/main.rs
1struct User {2    active: bool,3    username: String,4    email: String,5    sign_in_count: u64,6}78fn main() {9    let user1 = User {10        active: true,11        username: String::from("user1"),12        email: String::from("user1@example.com"),
Warning
When using struct update syntax, fields whose types implement Copy (like bool, u64) are copied, while fields that do not implement Copy (like String) are moved. After the move, the original instance may no longer be fully valid.

Tuple Structs

Tuple structs look like regular structs but their fields have no names — only types. They are useful when you want to give the whole tuple a distinct name and differentiate it from other tuples with the same type structure. Each tuple struct is its own distinct type.

src/main.rs
1// Tuple structs — same field types but different named types2struct Color(i32, i32, i32);3struct Point(f64, f64, f64);45fn main() {6    let black  = Color(0, 0, 0);7    let white  = Color(255, 255, 255);8    let origin = Point(0.0, 0.0, 0.0);910    // Access fields by index using dot notation11    println!("R={}, G={}, B={}", black.0, black.1, black.2);12    println!("X={}, Y={}, Z={}", origin.0, origin.1, origin.2);

Unit-Like Structs

Unit-like structs have no fields. They are useful when you need to implement a trait on a type but have no data to store in the type itself. They behave similarly to the unit type ().

src/main.rs
1// A unit-like struct — no fields, no parentheses2struct AlwaysEqual;3struct Marker;45// They can implement traits — a very common use case6impl std::fmt::Display for AlwaysEqual {7    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {8        write!(f, "AlwaysEqual")9    }10}1112fn main() {

Method Syntax

Methods are similar to functions but are defined within the context of a struct. The first parameter of a method is always self, representing the struct instance the method is called on. Methods are defined within an impl (implementation) block.

src/main.rs
1#[derive(Debug)]2struct Rectangle {3    width: f64,4    height: f64,5}67impl Rectangle {8    // &self borrows the instance immutably — most common9    fn area(&self) -> f64 {10        self.width * self.height11    }12
Info
Rust has automatic referencing and dereferencing. When you call rect.area(), Rust automatically adds &, &mut, or * to match the method signature. This is why you do not need to write (&rect).area().

Associated Functions

All functions within an impl block are associated functions because they are associated with that type. Associated functions that do not take self as the first parameter (not methods) are often used as constructors. They are called using the Typename:: syntax instead of dot notation.

src/main.rs
1#[derive(Debug)]2struct Rectangle {3    width: f64,4    height: f64,5}67impl Rectangle {8    // Associated function (constructor) — no self parameter9    fn new(width: f64, height: f64) -> Self {10        Self { width, height }11    }12
Tip
Self (capitalized) is an alias for the type currently being implemented within the impl block. Using Self instead of repeating the type name makes code easier to maintain — if you rename the struct, you do not need to update the inside of the impl block.

Multiple impl Blocks

Each struct can have multiple impl blocks. This is valid and sometimes useful for organizing code into logical groups. It is especially common when implementing traits (covered in the Generics and Traits chapter).

src/main.rs
1#[derive(Debug)]2struct Rectangle {3    width: f64,4    height: f64,5}67// First impl block: constructors and basic calculations8impl Rectangle {9    fn new(width: f64, height: f64) -> Self {10        Self { width, height }11    }12

Debug Printing with #[derive(Debug)]

Rust does not automatically provide printing for structs. To print a struct using {:?}or {:#?}, you need to opt in to the Debug trait by adding the #[derive(Debug)] attribute before the struct definition.

src/main.rs
1#[derive(Debug)] // automatically derives the Debug trait2struct Rectangle {3    width: f64,4    height: f64,5}67#[derive(Debug)]8struct Point {9    x: f64,10    y: f64,11}12
  • #[derive(Debug)]: This attribute instructs the compiler to automatically generate a Debug trait implementation based on the struct's fields.
  • {:?}: Compact debug format, suitable for small structs or single-line output.
  • {:#?}: Pretty-printed debug format with indentation, more readable for complex or nested structs.
  • dbg!: A macro that prints a value to stderr with file and line number, then returns ownership. Very useful for inspecting temporary values during debugging.
Info
#[derive(Debug)] requires that all fields of the struct also implement the Debug trait. Most of Rust's built-in types already implement Debug, so this almost always works immediately without any extra effort.

Key Takeaways

Điểm Chính

  • Structs group related data into named fieldsStruct nhóm dữ liệu liên quan thành các trường có tên
  • impl blocks define methods and associated functions for structsKhối impl định nghĩa phương thức và hàm liên kết cho struct
  • Tuple structs have unnamed fields accessed by indexTuple struct có trường không tên, truy cập bằng chỉ mục
  • The self parameter determines if a method borrows or consumes the structTham số self xác định phương thức mượn hay tiêu thụ struct

Practice

Test your understanding of this chapter

Quiz

What is the first parameter of a method in an impl block always named (by convention) and what does it represent?

Tham số đầu tiên của một phương thức trong khối impl luôn được đặt tên gì (theo quy ước) và nó đại diện cho điều gì?

True or False

In Rust, you can mark individual struct fields as mutable while leaving other fields immutable.

Trong Rust, bạn có thể đánh dấu từng trường của struct là có thể thay đổi trong khi các trường khác là bất biến.

Code Challenge

Call an associated function

Gọi một hàm liên kết (associated function)

struct Circle {
    radius: f64,
}

impl Circle {
    fn new(radius: f64) -> Self {
        Self { radius }
    }
    fn area(&self) -> f64 {
        std::f64::consts::PI * self.radius * self.radius
    }
}

fn main() {
    let c = Circlenew(5.0);
    println!("area: {}", c.area());
}
Quiz

Which syntax is correct for calling an associated function named 'new' on a struct named 'Point'?

Cú pháp nào đúng để gọi hàm liên kết tên 'new' trên struct tên 'Point'?

True or False

A struct in Rust can have multiple impl blocks, and this is sometimes useful for organizing methods into logical groups.

Một struct trong Rust có thể có nhiều khối impl, và điều này đôi khi hữu ích để tổ chức phương thức thành các nhóm logic.

Chapter Complete!

Great job! Keep the momentum going.

Your progress0 of 19 chapters read
← → to navigate chapters
Built: 4/8/2026, 12:01:11 PM