ToolkitX
知识库工具箱

结构体与枚举

struct, enum, match 模式匹配

20min·入门

01. 结构体定义和使用

Rust 有三种结构体:普通结构体(命名字段)、元组结构体(像有名字的元组)、单元结构体(没有字段,像标记)。普通结构体最常用,字段用冒号赋值。更新结构体可以用结构体更新语法 ..,从另一个实例复制剩余字段。
rust
// 普通结构体
struct User {
    name: String,
    email: String,
    age: u32,
    active: bool,
}

let user1 = User {
    name: String::from("小明"),
    email: String::from("[email protected]"),
    age: 18,
    active: true,
};

// 结构体更新语法
let user2 = User {
    name: String::from("小红"),
    ..user1 // 其余字段从 user1 复制
};

// 元组结构体
struct Point(i32, i32, i32);
let p = Point(0, 0, 0);

// 单元结构体
struct AlwaysEqual;
let subject = AlwaysEqual;
结构体更新语法 ..user1 会转移 String 类型字段的所有权,user1 可能之后不能用。

02. 方法:impl 块

Rust 的方法在 impl 块里定义,第一个参数是 &self(不可变引用)、&mut self(可变引用)或 self(所有权转移)。关联函数(静态方法)没有 self 参数,用 Type::func() 调用,常用作构造函数。每个结构体可以有多个 impl 块,分开管理不同功能。
rust
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // 关联函数(构造函数)
    fn new(width: u32, height: u32) -> Self {
        Self { width, height }
    }
    
    // 方法:计算面积
    fn area(&self) -> u32 {
        self.width * self.height
    }
    
    // 可变引用方法
    fn scale(&mut self, factor: u32) {
        self.width *= factor;
        self.height *= factor;
    }
    
    // 判断能否包含另一个矩形
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

let r = Rectangle::new(30, 50);
println!("面积: {}", r.area());
Rust 没有继承,但 trait 和组合提供了类似的代码复用能力。

03. 泛型结构体

泛型让结构体可以存储不同类型的数据。在结构体名后面加 <T>,字段类型用 T。impl 块也要带泛型 <T>。多个泛型参数用逗号分隔,如 <T, U>。泛型不影响运行时性能,因为 Rust 会在编译时做单态化,为每个具体类型生成专用代码。
rust
struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn new(x: T, y: T) -> Self {
        Self { x, y }
    }
}

// 只对特定类型实现方法
impl Point<f64> {
    fn distance_from_origin(&self) -> f64 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

// 多个泛型参数
struct Pair<T, U> {
    first: T,
    second: U,
}

let p1 = Point { x: 5, y: 10 };
let p2 = Point { x: 1.5, y: 3.2 };
let pair = Pair { first: "hello", second: 42 };
泛型代码写一遍,编译时自动为每个使用到的具体类型生成代码,零运行时开销。

04. 生命周期标注

生命周期是 Rust 独有的概念,当函数返回引用时,编译器需要知道这个引用能活多久。大部分时候编译器能自动推断(生命周期省略规则),推断不出来时才需要手动标注。生命周期语法是单引号加名字,如 'a,标注在引用类型前面:&'a str。标注不改变代码逻辑,只是帮助编译器做借用检查。
rust
// 需要生命周期标注的情况
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}
// 'a 表示返回的引用和 x、y 中较短的那个活得一样久

fn main() {
    let s1 = String::from("短");
    let s2 = String::from("长一点");
    
    let result = longest(&s1, &s2);
    println!("较长的: {}", result);
    
    // 结构体中的引用必须标注生命周期
    struct Excerpt<'a> {
        part: &'a str,
    }
}
生命周期省略规则覆盖了大部分常见情况,真的需要手写生命周期的时候远比想象中少。

05. 常用的派生宏

#[derive(...)] 是 Rust 最常用的属性,编译器自动帮结构体实现指定的 trait。#[derive(Debug)] 让结构体可以用 {:?} 打印调试输出。#[derive(Clone)] 实现深拷贝。#[derive(PartialEq)] 支持 == 比较。#[derive(Default)] 提供 Default::default()。大多数结构体第一行就是大长串的 derive。
rust
#[derive(Debug, Clone, PartialEq, Default)]
struct User {
    name: String,
    age: u32,
}

fn main() {
    let u1 = User { name: "小明".into(), age: 18 };
    let u2 = u1.clone();
    
    // Debug 打印
    println!("{:?}", u1);       // 紧凑格式
    println!("{:#?}", u1);      // 美化格式
    
    // PartialEq 支持比较
    assert_eq!(u1, u2);
    
    // Default
    let u3 = User::default(); // name="", age=0
}
几乎所有 struct 都至少会 derive Debug,调试时不能打印结构体太折磨了。

知识测验

1/4正确 0

Rust 中 impl 块的第一个参数 &self 表示什么?

下一节

集合类型

下一节