Rust: Rust 结构体(Struct):定义、实例化与方法实现

最后更新:2026-08-26

结构体(Struct)是 Rust 中自定义数据类型的核心工具——它把相关数据组合成一个有意义的整体。

如果说变量是"一个数据",那结构体就是"一组相关的数据"。它就像一张表格——每一行是一个实例,每一列是一个字段。


1. 你将学到


2. 概念图解

100%
flowchart LR
    subgraph "struct 定义"
        DEF["struct User"]
        F1["username: String"]
        F2["email: String"]
        F3["active: bool"]
        F4["sign_in_count: u64"]
    end
    subgraph "实例化"
        INS["let user = User { ... }"]
        V1["username: 'alice'"]
        V2["email: 'alice@example.com'"]
        V3["active: true"]
        V4["sign_in_count: 1"]
    end
    F1 -.->|"字段映射"| V1
    F2 -.->|"字段映射"| V2
    F3 -.->|"字段映射"| V3
    F4 -.->|"字段映射"| V4

3. 一个学生管理系统的故事

(1) 痛苦:用散落的变量管理学生

Maria 是一名老师,她需要管理班级学生的信息。每个学生有:

最开始她这样写:

RUST
let name1 = "小明";
let age1 = 18;
let grade1 = "高三";
let gpa1 = 3.8;

let name2 = "小红";
let age2 = 17;
let grade2 = "高二";
let gpa2 = 3.5;

当学生数量增加到 40 人时,这些散落的变量根本没法管理。打印成绩单要写 40 行代码,修改一个字段要在 4 个地方改。

(2) Rust 结构体的方案

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!("{}: {} 岁, 年级: {}, GPA: {:.1}",
        student1.name, student1.age, student1.grade, student1.gpa);
    println!("{}: {} 岁, 年级: {}, GPA: {:.1}",
        student2.name, student2.age, student2.grade, student2.gpa);
}

结构体像一个"模具"——定义了"学生"应该有哪些信息。每一份实例就是一份填写好的表格。字段通过 . 访问,清晰且安全。


4. 结构体类型

100%
graph TB
    A[struct 类型] --> B[命名结构体: 字段有名有类型]
    A --> C[元组结构体: 字段只有类型没有名字]
    A --> D[单元结构体: 没有任何字段]
    B --> E[struct User { name: String, age: u8 }]
    C --> F[struct Color(i32, i32, i32)]
    D --> G[struct EmptyMarker]
类型 定义方式 适用场景
命名结构体 struct S { f1: T, f2: T } 大多数场景,字段名自带文档
元组结构体 struct S(T1, T2) 包装单一概念(如颜色 RGB、坐标)
单元结构体 struct S; 类型标记、trait 实现占位

(2) 方法 vs 关联函数

类型 第一个参数 调用方式 所有权 典型用途
方法 &self obj.method() 借用 读取实例数据
方法 &mut self obj.method() 可变借用 修改实例数据
方法 self obj.method() 消费 转换/销毁实例
关联函数 Type::function() 构造函数、工厂方法

(3) 常用派生 trait 速查

Trait 功能 自动条件 示例
Debug {:?} 格式化打印 所有字段实现 Debug #[derive(Debug)]
Clone .clone() 深拷贝 所有字段实现 Clone #[derive(Clone)]
Copy 赋值自动复制 Clone + 所有字段 Copy #[derive(Copy, Clone)]
PartialEq == / != 比较 所有字段实现 PartialEq #[derive(PartialEq)]
Hash 用作 HashMap 键 所有字段实现 Hash #[derive(Hash)]
Default 默认值 所有字段有 Default #[derive(Default)]

5. 结构体示例

▶ 示例 1:命名结构体的完整用法(难度 ⭐)

RUST
// ============================================
// 命名结构体:定义、实例化、字段访问
// ============================================

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

fn main() {
    // 创建实例
    let rect = Rectangle {
        width: 30,
        height: 50,
    };

    // 访问字段
    println!("宽度: {}, 高度: {}", rect.width, rect.height);

    // 可变实例
    let mut mutable_rect = Rectangle {
        width: 10,
        height: 20,
    };
    mutable_rect.width = 15;  // ✅ 可以修改
    println!("修改后宽度: {}", mutable_rect.width);

    // 用 Debug 打印
    println!("rect: {:?}", rect);
    println!("rect (pretty): {:#?}", rect);
}

输出:

TEXT 📖 仅展示
宽度: 30, 高度: 50
修改后宽度: 15
rect: Rectangle { width: 30, height: 50 }
rect (pretty): Rectangle {
    width: 30,
    height: 50,
}

#[derive(Debug)] 让结构体可以用 {:?}{:#?} 格式化打印。这是调试时最常用的派生 trait 之一。


▶ 示例 2:struct 更新语法与字段简写(难度 ⭐⭐)

RUST
// ============================================
// 字段简写初始化 + 更新语法
// ============================================

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

fn build_user(username: String, email: String) -> User {
    User {
        username,   // 字段名和变量名相同时可简写
        email,      // 等价于 email: email
        active: true,
        sign_in_count: 1,
    }
}

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

    // 更新语法:基于 user1 创建 user2,只修改 email 和 username
    let user2 = User {
        email: String::from("alice_new@example.com"),
        username: String::from("alice_new"),
        ..user1  // 其他字段从 user1 复制(注意:String 字段会 move!)
    };
    // println!("user1: {}", user1.username);  // ❌ username 被 move 到 user2

    println!("user2: {:?}", user2);
    println!("user1.active: {}", user1.active);  // ✅ bool 是 Copy 的,还在
}

输出:

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

字段简写:当变量名和结构体字段名一致时,可以省略 field: field 中的冒号。更新语法 .. 从另一个实例复制剩余字段——注意复制的是 move 语义而不是 Clone。


▶ 示例 3:impl 块——给结构体添加方法(难度 ⭐⭐)

RUST
// ============================================
// impl 块:方法(&self)和关联函数(无 &self)
// ============================================

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

impl Rectangle {
    // 方法:&self 是 Rectangle 实例的引用
    fn area(&self) -> u32 {
        self.width * self.height
    }

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

    // 关联函数(类似静态方法):没有 &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);  // 关联函数用 :: 调用

    println!("rect1 面积: {}", rect1.area());  // 1500
    println!("rect1 能否容纳 rect2: {}", rect1.can_hold(&rect2));  // true
    println!("正方形面积: {}", square.area());  // 225
}

输出:

TEXT 📖 仅展示
rect1 面积: 1500
rect1 能否容纳 rect2: true
正方形面积: 225

方法(Method)的第一个参数是 &self(或 &mut self),通过 . 调用。关联函数(Associated Function)没有 &self,通过 :: 调用——经典的例子是 String::from()


▶ 示例 4:元组结构体(难度 ⭐⭐)

RUST
// ============================================
// 元组结构体:适合"就是一堆值"的场景
// ============================================

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

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

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

    // 字段通过索引访问(像元组)
    println!("黑色: R={}, G={}, B={}", black.0, black.1, black.2);
    println!("原点: x={}, y={}, z={}", origin.0, origin.1, origin.2);

    // 元组结构体和元组结构体不一样!
    // let wrong: Color = origin;  // ❌ 编译错误:Point 不能赋值给 Color

    // 虽然内部结构相同,但它们是不同的类型
    // 这防止了"颜色和坐标搞混"的 bug
}

输出:

TEXT 📖 仅展示
黑色: R=0, G=0, B=0
原点: x=0, y=0, z=0

元组结构体(Tuple Struct)没有字段名,只有类型。它和元组类似,但有自己的类型名。Color(0,0,0)Point(0,0,0) 内部相同但类型不同——防止混用。


▶ 示例 5:综合练习——学生管理系统(难度 ⭐⭐⭐)

RUST
// ============================================
// 综合示例:结构体方法、关联函数与更新语法
// ============================================

#[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!("{} ({}岁) 平均: {:.1} 等级: {}", 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=== 班级排名 ===");
    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());
    }
}

输出:

TEXT 📖 仅展示
Alice (20岁) 平均: 91.7 等级: A
Bob (21岁) 平均: 65.0 等级: D
Charlie (20岁) 平均: 93.8 等级: A

=== 班级排名 ===
第1名: Charlie (20岁) 平均: 93.8 等级: A
第2名: Alice (20岁) 平均: 91.7 等级: A
第3名: Bob (21岁) 平均: 65.0 等级: D

此示例综合运用了关联函数 new()、方法 add_score()/average()/grade()、更新语法 ..alice.clone()#[derive(Debug, Clone)]。结构体是组织相关数据和方法的核心工具。


❓ 常见问题

Q struct 的字段所有权怎么处理?
A 结构体本身遵循所有权的规则。
Q 方法(&self)和关联函数(无 &self)什么时候用?
A 需要操作实例数据时用方法,不需要实例数据时用关联函数。
Q &self 和 self 有什么区别?
A &self 是借用(不转移所有权),self 是消费实例(转移所有权)。
Q struct 能存引用吗?
A 可以,但必须标注生命周期。
Q #[derive(Debug)] 是干什么的?
A 它自动为结构体实现 Debug trait。

📖 小节


📝 作业

  1. 难度 ⭐:定义一个结构体 Book,包含 title: Stringauthor: Stringyear: u32。创建两个实例并打印标题和作者。
  2. 难度 ⭐⭐:给 Rectangle 添加一个方法 fn perimeter(&self) -> u32 计算周长。添加一个关联函数 fn from_width(w: u32) -> Rectangle 创建一个宽高相等的矩形。
  3. 难度 ⭐⭐⭐:定义一个元组结构体 Distance(f64, f64) 表示距离(米、厘米)。实现方法 fn to_meters(&self) -> f64 返回总米数(如 Distance(1, 50) 返回 1.50)。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

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

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