复合类型与模式匹配 beginner 30 分钟 更新 2026-06-15

结构体与方法

用 struct 给数据建模,用 impl 定义方法和关联函数。

结构体与方法

你会学到什么

  • struct 把相关字段组合成一个类型。
  • impl 块定义方法(带 self)和关联函数(不带 self)。
  • #[derive(...)] 自动获得 DebugClonePartialEq 等能力。

最小示例

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

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

运行代码

cd examples
cargo run -p rt_10_structs
cargo test -p rt_10_structs

代码讲解

关联函数常用作构造器,用 Self 指代当前类型:

fn new(width: u32, height: u32) -> Self {
    Self { width, height }
}

方法的第一个参数是 &self(只读)、&mut self(可修改)或 self(消费所有权)。

#[derive(Debug, Clone, PartialEq)] 让结构体能被 {:?} 打印、.clone() 复制、用 == 比较,省去手写样板代码。

常见错误

忘记 & 直接写 self,会意外消费所有权:

impl Rectangle {
    fn area(self) -> u32 { // 调用后 rectangle 被移动
        self.width * self.height
    }
}

只读方法几乎总是用 &self

练习

  • Rectangle 加一个 can_hold(&self, other: &Rectangle) -> bool
  • 加一个关联函数 square(size: u32) -> Rectangle

小结

结构体是 Rust 建模的基本单元,方法和关联函数组织行为,derive 自动生成常用 trait 实现。

下一步

当数据有“多种形态”时,枚举比结构体更合适。下一章学习枚举与 Option。

完整示例代码

下面是 examples/10_structs/ 的完整源码。无需 clone 仓库,直接在页面上阅读、复制、对照运行。

examples/10_structs/src/main.rs
//! 结构体、方法与关联函数。

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

impl Rectangle {
    /// 关联函数(无 self),常用作构造器。
    fn new(width: u32, height: u32) -> Self {
        Self { width, height }
    }

    /// 方法(带 &self),只读访问字段。
    fn area(&self) -> u32 {
        self.width * self.height
    }

    fn is_square(&self) -> bool {
        self.width == self.height
    }

    /// 返回一个新结构体,不修改自身。
    fn scaled(&self, factor: u32) -> Self {
        Self::new(self.width * factor, self.height * factor)
    }
}

fn main() {
    let rect = Rectangle::new(3, 4);
    println!("{rect:?}");
    println!("area = {}", rect.area());
    println!("is_square = {}", rect.is_square());
    println!("scaled = {:?}", rect.scaled(2));
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn computes_area() {
        assert_eq!(Rectangle::new(3, 4).area(), 12);
    }

    #[test]
    fn detects_square() {
        assert!(Rectangle::new(5, 5).is_square());
        assert!(!Rectangle::new(5, 6).is_square());
    }

    #[test]
    fn scales_into_new_value() {
        let rect = Rectangle::new(2, 3);
        assert_eq!(rect.scaled(2), Rectangle::new(4, 6));
        // 原值不变。
        assert_eq!(rect, Rectangle::new(2, 3));
    }
}
examples/10_structs/Cargo.toml
[package]
name = "rt_10_structs"
version.workspace = true
edition.workspace = true
publish.workspace = true