Rust: Rust Structures (Struct)

Last updated: 2026-08-26

Structures (Struct) are the core tool for defining custom data types in Rust—they group related data into a meaningful whole.

If a variable is "a single piece of data," then a structure is "a set of related data." It’s like a table—each row is an instance, and each column is a field.


1. What You'll Learn



2. Conceptual Diagrams

100%
flowchart LR
    subgraph "struct Definition"
        DEF["struct User"]
        F1["username: String"]
        F2["email: String"]
        F3["active: bool"]
        F4["sign_in_count: u64"]
    end
    subgraph "Instantiation"
        INS["let user = User { ... }"]
        V1["username: 'alice'"]
        V2["email: 'alice@example.com'"]
        V3["active: true"]
        V4["sign_in_count: 1"]
    end
    F1 -.->|"Field Mapping"| V1
    F2 -.->|"Field Mapping"| V2
    F3 -.->|"Field Mapping"| V3
    F4 -.->|"Field Mapping"| V4


3. The Story of a Student Management System

(1) The Struggle: Managing Students with Scattered Variables

Maria is a teacher who needs to manage information about the students in her class. Each student has:

At first, she wrote:

RUST
let name1 = "Xiao Ming";
let age1 = 18;
let grade1 = "Senior Year";
let gpa1 = 3.8;

let name2 = "Xiao Hong";
let age2 = 17;
let grade2 = "11th Grade";
let gpa2 = 3.5;

When the number of students increases to 40, these scattered variables become completely unmanageable. Printing a transcript requires writing 40 lines of code, and changing a single field means making changes in four different places.

(2) The Rust struct approach

RUST
struct Student {
    name: String,
    age: u8,
    grade: String,
    gpa: f64,
}

fn main() {
    let student1 = Student {
        name: String::from("Xiao Ming"),
        age: 18,
        grade: String::from("Senior 3"),
        gpa: 3.8,
    };

    let student2 = Student {
        name: String::from("Xiao Hong"),
        age: 17,
        grade: String::from("Senior 2"),
        gpa: 3.5,
    };

    println!("{}: {} years old, Grade: {}, GPA: {:.1}",
        student1.name, student1.age, student1.grade, student1.gpa);
    println!("{}: {} years old, Grade: {}, GPA: {:.1}",
        student2.name, student2.age, student2.grade, student2.gpa);
}

A struct is like a "template"—it defines what information a "student" should contain. Each instance is like a completed form. Fields are accessed via ., which is clear and secure.



4. Structure Types

100%
graph TB
    A[struct Type] --> B[Naming Structures: Fields have names and types]
    A --> C[Tuple Structure: The field has a type but no name]
    A --> D[Unit Structure: No fields]
    B --> E[struct User { name: String, age: u8 }]
    C --> F[struct Color(i32, i32, i32)]
    D --> G[struct EmptyMarker]
Type Definition Use Cases
Naming Conventions struct S { f1: T, f2: T } In most cases, field names are self-documenting
Tuple Structure struct S(T1, T2) Encapsulates a single concept (e.g., RGB color, coordinates)
Unit Structure struct S; Type marker, placeholder for trait implementation

(2) Methods vs. Association Functions

Type First Parameter Calling Convention Ownership Typical Uses
Method &self obj.method() Borrow Read instance data
Method &mut self obj.method() Variable Borrowing Modify Instance Data
Method self obj.method() Consumption Convert/Destroy Instance
Related Functions None Type::function() Constructors, Factory Methods

(3) Quick Reference for Commonly Used Derived Traits

Trait Functionality Automatic Conditions Example
Debug {:?} Formatted Print Debug All Fields #[derive(Debug)]
Clone .clone() Deep copy Clone all fields #[derive(Clone)]
Copy Automatic Copy on Assignment Clone + Copy All Fields #[derive(Copy, Clone)]
PartialEq == / != Comparison All fields implement PartialEq #[derive(PartialEq)]
Hash Used as a HashMap key All fields implement Hash #[derive(Hash)]
Default Default value All fields have a default #[derive(Default)]


5. Structure Examples

▶ Example 1: Complete Usage of Named Structures (Difficulty ⭐)

Output:

TEXT 📖 Display only
Width: <rect.width>, Height: <rect.height>
Width after modification: <mutable_rect.width>
rect: <rect>
rect (pretty): <rect>
RUST
// ============================================
// Naming Structures: Definition, Instantiation, Field Access
// ============================================

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    // Create an Instance
    let rect = Rectangle {
        width: 30,
        height: 50,
    };

    // Access Fields
    println!("Width: {}, Height: {}", rect.width, rect.height);

    // Variable Instances
    let mut mutable_rect = Rectangle {
        width: 10,
        height: 20,
    };
    mutable_rect.width = 15;  // ✅ Can be modified
    println!("Width after modification: {}", mutable_rect.width);

    // Use Debug Print
    println!("rect: {:?}", rect);
    println!("rect (pretty): {:#?}", rect);
}

Output:

TEXT 📖 Display only
Width: 30, Height: 50
Width after modification: 15
rect: Rectangle { width: 30, height: 50 }
rect (pretty): Rectangle {
    width: 30,
    height: 50,
}

#[derive(Debug)] Allows structs to be printed using the {:?} or {:#?} formatting. This is one of the most commonly used derived traits during debugging.


▶ Example 2: Syntax for Updating Structures and Field Shorthand (Difficulty ⭐⭐)

Output:

TEXT 📖 Display only
user1: <user1.username>
user2: <user2>
user1.active: <user1.active>
RUST
// ============================================
// Initializing Field Abbreviations + Update Syntax
// ============================================

#[derive(Debug)]
struct User {
    username: String,
    email: String,
    active: bool,
    sign_in_count: u64,
}

fn build_user(username: String, email: String) -> User {
    User {
        username,   // When field names and variable names are the same, they can be written in shorthand.
        email,      // Equivalent to email: email
        active: true,
        sign_in_count: 1,
    }
}

fn main() {
    let user1 = build_user(
        String::from("alice"),
        String::from("alice@example.com"),
    );

    // Update Syntax: Based on user1 create user2, modify only email and username
    let user2 = User {
        email: String::from("alice_new@example.com"),
        username: String::from("alice_new"),
        ..user1  // Other fields copied from user1 (Note: String fields will move!)
    };
    // println!("user1: {}", user1.username);  // ❌ username was moved to user2

    println!("user2: {:?}", user2);
    println!("user1.active: {}", user1.active);  // ✅ bool is Copy, still valid
}

Output:

TEXT 📖 Display only
user2: User { username: "alice_new", email: "alice_new@example.com", active: true, sign_in_count: 1 }
user1.active: true

Field Abbreviation: When a variable name matches a struct field name, the colon in field: field can be omitted. Updated syntax .. copies the remaining fields from another instance—note that this uses move semantics rather than Clone.


▶ Example 3: The impl block—Adding methods to a struct (Difficulty: ⭐⭐)

Output:

TEXT 📖 Display only
rect1 Area: <rect1.area()>
rect1 Can it accommodate? rect2: <rect1.can_hold(&rect2)>
Area of a Square: <square.area()>
RUST
// ============================================
// impl block: Methods (&self) and associated functions (no &self)
// ============================================

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // Methods: &self is a reference to a Rectangle instance
    fn area(&self) -> u32 {
        self.width * self.height
    }

    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }

    // Associative Functions (similar to static methods): no &self
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    let rect2 = Rectangle {
        width: 10,
        height: 20,
    };
    let square = Rectangle::square(15);  // Correlation functions are used to :: Call

    println!("rect1 Area: {}", rect1.area());  // 1500
    println!("rect1 Can it accommodate? rect2: {}", rect1.can_hold(&rect2));  // true
    println!("Area of a Square: {}", square.area());  // 225
}

Output:

TEXT 📖 Display only
rect1 Area: 1500
rect1 Can it accommodate? rect2: true
Area of a Square: 225

The first parameter of a method (Method) is &self (or &mut self), and it is called via .. An associated function (Associated Function) does not have &self and is called via ::—a classic example is String::from().


▶ Example 4: Tuple Structures (Difficulty ⭐⭐)

Output:

TEXT 📖 Display only
Black: R=<black.0>, G=<black.1>, B=<black.2>
The Beginning: x=<origin.0>, y=<origin.1>, z=<origin.2>
RUST
// ============================================
// Tuple Structure: Suitable for "it's just a bunch of values" scenarios
// ============================================

#[derive(Debug)]
struct Color(i32, i32, i32);  // RGB Color

#[derive(Debug)]
struct Point(i32, i32, i32);  // 3D Coordinates

fn main() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);

    // Fields Accessed via Indexes (tuple-like)
    println!("Black: R={}, G={}, B={}", black.0, black.1, black.2);
    println!("The Beginning: x={}, y={}, z={}", origin.0, origin.1, origin.2);

    // Tuple structures are not the same as tuples!
    // let wrong: Color = origin;  // ❌ Compilation Error: Point cannot be assigned to Color

    // Although their internal structures are the same, they are different types
    // This prevents "mixing up colors and coordinates" bugs
}

Output:

TEXT 📖 Display only
<alice.summary()>
<bob.summary()>
<charlie.summary()>

=== Class Rankings ===
#<i + 1>: <s.summary()>

A tuple struct has no field names, only types. It is similar to a tuple but has its own type name. Color(0,0,0) and Point(0,0,0) are identical internally but have different types—this prevents confusion.


▶ Example 5: Comprehensive Exercise—Student Management System (Difficulty ⭐⭐⭐)

Output:

TEXT 📖 Display only
<alice.summary()>
<bob.summary()>
<charlie.summary()>

=== Class Rankings ===
#<i + 1>: <s.summary()>
RUST
// ============================================
// Comprehensive Example: Structure-Based Approach, Associated Functions and Update Syntax
// ============================================

#[derive(Debug, Clone)]
struct Student {
    name: String,
    age: u8,
    scores: Vec<u32>,
}

impl Student {
    fn new(name: &str, age: u8) -> Self {
        Student {
            name: name.to_string(),
            age,
            scores: Vec::new(),
        }
    }

    fn add_score(&mut self, score: u32) {
        self.scores.push(score);
    }

    fn average(&self) -> f64 {
        if self.scores.is_empty() {
            return 0.0;
        }
        let sum: u32 = self.scores.iter().sum();
        sum as f64 / self.scores.len() as f64
    }

    fn grade(&self) -> char {
        match self.average() as u32 {
            90..=100 => 'A',
            80..=89  => 'B',
            70..=79  => 'C',
            60..=69  => 'D',
            _        => 'F',
        }
    }

    fn summary(&self) -> String {
        format!("{} ({} years old) Average: {:.1} Level: {}", self.name, self.age, self.average(), self.grade())
    }
}

fn main() {
    let mut alice = Student::new("Alice", 20);
    alice.add_score(95);
    alice.add_score(88);
    alice.add_score(92);
    println!("{}", alice.summary());

    let mut bob = Student::new("Bob", 21);
    bob.add_score(72);
    bob.add_score(65);
    bob.add_score(58);
    println!("{}", bob.summary());

    let mut charlie = Student {
        name: String::from("Charlie"),
        ..alice.clone()
    };
    charlie.name = String::from("Charlie");
    charlie.add_score(100);
    println!("{}", charlie.summary());

    let students = [&alice, &bob, &charlie];
    println!("\n=== Class Rankings ===");
    let mut ranked: Vec<_> = students.iter().collect();
    ranked.sort_by(|a, b| b.average().partial_cmp(&a.average()).unwrap());
    for (i, s) in ranked.iter().enumerate() {
        println!("#{}: {}", i + 1, s.summary());
    }
}

Output:

TEXT 📖 Display only
Alice (20 years old) Average: 91.7 Level: A
Bob (21 years old) Average: 65.0 Level: D
Charlie (20 years old) Average: 93.8 Level: A

=== Class Rankings ===
#1: Charlie (20 years old) Average: 93.8 Level: A
#2: Alice (20 years old) Average: 91.7 Level: A
#3: Bob (21 years old) Average: 65.0 Level: D

This example combines the use of the association function new(), the methods add_score()/average()/grade(), and the update syntax ..alice.clone() and #[derive(Debug, Clone)]. Structures are the core tools for organizing related data and methods.


❓ FAQ

Q How is ownership of struct fields handled?
A The struct itself follows the rules of ownership.
Q When should you use methods (with &self) and associated functions (without &self)?
A Use methods when you need to manipulate instance data, and use associated functions when you don’t need instance data.
Q What is the difference between &self and self?
A &self is a reference (no transfer of ownership), while self consumes the instance (transfer of ownership).
Q Can a struct store a reference?
A Yes, but you must specify its lifetime.
Q What does #[derive(Debug)] do?
A It automatically implements the Debug trait for structs.

📖 Summary


📝 Exercises

  1. Difficulty ⭐: Define a struct Book that contains title: String, author: String, and year: u32. Create two instances and print the title and author.
  2. Difficulty ⭐⭐: Add a method fn perimeter(&self) -> u32 to Rectangle to calculate the perimeter. Add an associated function fn from_width(w: u32) -> Rectangle to create a rectangle with equal width and height.
  3. Difficulty ⭐⭐⭐: Define a tuple structure Distance(f64, f64) to represent distances (in meters and centimeters). Implement a method fn to_meters(&self) -> f64 that returns the total number of meters (for example, Distance(1, 50) returns 1.50).
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏