阶段三:结构体与枚举

74 阅读3分钟

阶段三:结构体与枚举

核心知识点

  1. 结构体(Struct)

    • 自定义复合数据类型,包含多个命名字段
    • 通过 impl 块为结构体添加方法
    • 关联函数(类似静态方法)
  2. 枚举(Enum)

    • 定义一组可能的值(变体)
    • 可携带数据(类似联合类型)
    • 模式匹配(match 表达式)
  3. 模式匹配(Pattern Matching)

    • match 处理枚举变体
    • 通配符 _ 和占位符 ..
    • if let 简化单分支匹配

学习步骤

1. 结构体定义与实例化

// 定义结构体
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

// 创建实例
let user1 = User {
    email: String::from("alice@example.com"),
    username: String::from("alice"),
    active: true,
    sign_in_count: 1,
};

2. 为结构体添加方法

impl User {
    // 关联函数(类似构造函数)
    fn new(email: String, username: String) -> Self {
        User {
            email,
            username,
            active: true,
            sign_in_count: 0,
        }
    }

    // 方法(第一个参数为 `&self`)
    fn deactivate(&mut self) {
        self.active = false;
    }
}

3. 枚举与模式匹配

// 定义枚举
enum WebEvent {
    PageLoad,                  // 无数据
    KeyPress(char),             // 携带字符
    Click { x: i64, y: i64 },   // 匿名结构体
}

// 处理枚举
fn handle_event(event: WebEvent) {
    match event {
        WebEvent::PageLoad => println!("页面加载"),
        WebEvent::KeyPress(c) => println!("按下键: {}", c),
        WebEvent::Click { x, y } => println!("点击坐标: ({}, {})", x, y),
    }
}

练习题

题目1:计算矩形面积

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // 实现方法 `area`
    fn area(&self) -> u32 {
        self.width * self.height
    }

    // 关联函数 `square` 创建正方形
    fn square(size: u32) -> Self {
        Rectangle {
            width: size,
            height: size,
        }
    }
}

fn main() {
    let rect = Rectangle { width: 30, height: 50 };
    println!("面积: {}", rect.area()); // 输出 1500

    let square = Rectangle::square(10);
    println!("正方形面积: {}", square.area()); // 输出 100
}

题目2:处理消息枚举

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(u8, u8, u8),
}

fn process_message(msg: Message) {
    match msg {
        Message::Quit => println!("退出程序"),
        Message::Move { x, y } => println!("移动到坐标 ({}, {})", x, y),
        Message::Write(text) => println!("文本消息: {}", text),
        Message::ChangeColor(r, g, b) => println!("颜色变更为 RGB({}, {}, {})", r, g, b),
    }
}

fn main() {
    let messages = vec![
        Message::Quit,
        Message::Move { x: 10, y: 20 },
        Message::Write(String::from("Hello Rust")),
        Message::ChangeColor(255, 0, 0),
    ];

    for msg in messages {
        process_message(msg);
    }
}

题目3:自定义 Option

// 实现一个简化版 Option<T>
enum MyOption<T> {
    MySome(T),
    MyNone,
}

impl<T> MyOption<T> {
    // 实现 unwrap 方法
    fn unwrap(self) -> T {
        match self {
            MyOption::MySome(val) => val,
            MyOption::MyNone => panic!("尝试解包 MyNone 值"),
        }
    }
}

fn main() {
    let some_val = MyOption::MySome(42);
    println!("解包结果: {}", some_val.unwrap()); // 输出 42

    let none_val: MyOption<i32> = MyOption::MyNone;
    // none_val.unwrap(); // 运行时 panic
}

常见错误与修复

  1. 错误:missing field 'email' in initializer

    • 原因:结构体实例化时缺少字段
    • 修复:补全所有字段或使用 .. 语法继承其他字段
      let user2 = User {
          email: String::from("bob@example.com"),
          username: String::from("bob"),
          ..user1 // 继承剩余字段
      };
      
  2. 错误:non-exhaustive patterns

    • 原因:match 未覆盖所有枚举变体
    • 修复:添加 _ 通配分支
      match msg {
          Message::Quit => { /* ... */ },
          _ => println!("其他消息类型"),
      }
      

扩展知识

  1. 元组结构体(Tuple Struct)

    • 无名字段的结构体,用于轻量级封装
    struct Point(i32, i32);
    let origin = Point(0, 0);
    
  2. 模式守卫(Pattern Guard)

    • match 分支中添加条件判断
    match value {
        Some(x) if x > 10 => println!("大于10"),
        Some(x) => println!("值为 {}", x),
        None => println!("无值"),
    }