【Rust入门知识点学与练】第13课:枚举 Enum

知识点1:定义和使用枚举

枚举让你定义一个类型,它只能是几种固定值之一:

rust 复制代码
// 定义枚举
enum Direction {
    Up,
    Down,
    Left,
    Right,
}

fn main() {
    // 创建枚举值
    let dir = Direction::Up;

    // 用 match 匹配(必须穷举所有情况)
    match dir {
        Direction::Up => println!("向上移动"),
        Direction::Down => println!("向下移动"),
        Direction::Left => println!("向左移动"),
        Direction::Right => println!("向右移动"),
    }

    // 用 if let 只匹配一种情况
    if let Direction::Up = dir {
        println!("当前方向是上");
    }
}

知识点2:带数据的枚举

枚举的每个变体可以携带不同类型的数据:

rust 复制代码
enum Message {
    Quit,                        // 无数据
    Move { x: i32, y: i32 },    // 携带具名字段
    Write(String),              // 携带一个 String
    ChangeColor(u8, u8, u8),    // 携带三个 u8
}

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

    for msg in msgs {
        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);
            }
        }
    }
}

知识点3:给枚举添加方法

rust 复制代码
enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

impl Coin {
    // 关联函数
    fn all() -> Vec<Coin> {
        vec![Coin::Penny, Coin::Nickel, Coin::Dime, Coin::Quarter]
    }

    // 方法:返回面值(美分)
    fn value(&self) -> u32 {
        match self {
            Coin::Penny => 1,
            Coin::Nickel => 5,
            Coin::Dime => 10,
            Coin::Quarter => 25,
        }
    }

    // 方法:返回名称
    fn name(&self) -> &str {
        match self {
            Coin::Penny => "一分钱",
            Coin::Nickel => "五分钱",
            Coin::Dime => "一角钱",
            Coin::Quarter => "二十五分钱",
        }
    }
}

fn main() {
    for coin in Coin::all() {
        println!("{} = {}美分", coin.name(), coin.value());
    }
}

知识点4:Option 和 Result(标准库中的枚举)

Rust 没有 null,用 Option 表示"可能有值,也可能没有":

rust 复制代码
fn find_first_even(numbers: &[i32]) -> Option<i32> {
    for &n in numbers {
        if n % 2 == 0 {
            return Some(n);   // 找到了,返回 Some(值)
        }
    }
    None  // 没找到,返回 None
}

fn main() {
    let nums = vec![1, 3, 5, 8, 9];
    match find_first_even(&nums) {
        Some(n) => println!("找到偶数: {}", n),
        None => println!("没有偶数"),
    }

    // 常用方法
    let result = find_first_even(&nums);
    println!("是否有值: {}", result.is_some());
    println!("取值或默认: {}", result.unwrap_or(0));
}

Result 用于可能出错的操作:

rust 复制代码
fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err(String::from("除数不能为零"))
    } else {
        Ok(a / b)
    }
}

fn main() {
    match divide(10.0, 3.0) {
        Ok(result) => println!("结果: {}", result),
        Err(e) => println!("错误: {}", e),
    }

    match divide(10.0, 0.0) {
        Ok(result) => println!("结果: {}", result),
        Err(e) => println!("错误: {}", e),
    }
}

核心规则

概念 写法

定义枚举 enum Name { A, B, C }

带数据变体 Variant(Type) 或 Variant { field: Type }

创建枚举值 Name::Variant 或 Name::Variant(data)

匹配枚举 match 必须穷举所有变体

只匹配一种 if let Name::Variant = value

通配符 _ => ... 匹配剩余情况

Option 有值 Some(value)

Option 无值 None

Result 成功 Ok(value)

Result 失败 Err(error)

动手试试

补全下面的代码:

rust 复制代码
// 补全:定义一个 Shape 枚举,包含以下变体:
// Circle(f64)              --- 半径
// Rectangle(f64, f64)      --- 宽和高
// Triangle(f64, f64, f64)  --- 三条边

// 补全:为 Shape 实现以下方法:
// 1. area(&self) -> f64
//    - Circle: π * r²(用 std::f64::consts::PI)
//    - Rectangle: 宽 * 高
//    - Triangle: 用海伦公式 √(s(s-a)(s-b)(s-c)),s = (a+b+c)/2
// 2. perimeter(&self) -> f64
//    - Circle: 2πr
//    - Rectangle: 2(宽+高)
//    - Triangle: a+b+c
// 3. describe(&self) -> String
//    返回格式如 "圆形(半径=5)" / "矩形(宽=3, 高=4)" / "三角形(边=3,4,5)"

fn main() {
    let shapes = vec![
        Shape::Circle(5.0),
        Shape::Rectangle(3.0, 4.0),
        Shape::Triangle(3.0, 4.0, 5.0),
    ];

    for shape in &shapes {
        // 补全:打印每个形状的描述、面积和周长
        // 面积保留2位小数,周长保留2位小数
    }

    // 期望输出:
    // 圆形(半径=5): 面积=78.54, 周长=31.42
    // 矩形(宽=3, 高=4): 面积=12.00, 周长=14.00
    // 三角形(边=3,4,5): 面积=6.00, 周长=12.00
}

期望输出:

圆形(半径=5): 面积=78.54, 周长=31.42

矩形(宽=3, 高=4): 面积=12.00, 周长=14.00

三角形(边=3,4,5): 面积=6.00, 周长=12.00

写好了贴过来,没问题就进入第14课:模式匹配 Pattern Matching 👋

贴代码

rust 复制代码
  
// 定义 Shape 枚举
#[derive(Debug)]
enum Shape {
    Circle(f64),              // 半径
    Rectangle(f64, f64),      // 宽和高
    Triangle(f64, f64, f64),  // 三条边
}

// 为 Shape 实现方法
impl Shape {
    // 1. 面积
    fn area(&self) -> f64 {
        match self {
            Shape::Circle(r) => std::f64::consts::PI * r * r,
            Shape::Rectangle(w, h) => w * h,
            Shape::Triangle(a, b, c) => {
                let s = (a + b + c) / 2.0;
                (s * (s - a) * (s - b) * (s - c)).sqrt()
            }
        }
    }

    // 2. 周长
    fn perimeter(&self) -> f64 {
        match self {
            Shape::Circle(r) => 2.0 * std::f64::consts::PI * r,
            Shape::Rectangle(w, h) => 2.0 * (w + h),
            Shape::Triangle(a, b, c) => a + b + c,
        }
    }

    // 3. 描述信息
    fn describe(&self) -> String {
        match self {
            Shape::Circle(r) => format!("圆形(半径={})", r),
            Shape::Rectangle(w, h) => format!("矩形(宽={}, 高={})", w, h),
            Shape::Triangle(a, b, c) => format!("三角形(边={},{},{})", a, b, c),
        }
    }
}

fn main() {
    let shapes = vec![
        Shape::Circle(5.0),
        Shape::Rectangle(3.0, 4.0),
        Shape::Triangle(3.0, 4.0, 5.0),
    ];

    for shape in &shapes {
        // 打印每个形状的描述、面积和周长,保留2位小数
        println!(
            "{}: 面积={:.2}, 周长={:.2}",
            shape.describe(),
            shape.area(),
            shape.perimeter()
        );
    }
}

运行输出

plaintext

圆形(半径=5): 面积=78.54, 周长=31.42

矩形(宽=3, 高=4): 面积=12.00, 周长=14.00

三角形(边=3,4,5): 面积=6.00, 周长=12.00

枚举核心知识点

  1. enum(枚举):一个类型可以代表多种不同的数据变体;变体里面可以直接存放数据(元组枚举)。
  2. match 模式匹配:处理枚举必须用 match,对每一种分支分别计算逻辑。
  3. std::f64::consts::PI :Rust 标准库提供的圆周率常量。
  4. 海伦公式: s=(a+b+c)/2 ,面积 = √s(s−a)(s−b)(s−c)。
  5. 格式化 {:.2} :浮点数保留两位小数输出。
  6. 遍历的时候 &shapes ,使用借用,不转移所有权。

对比结构体:struct 用来保存一种事物的数据;enum 用来表示多种不同类型的事物。

枚举练习完成!结构体+枚举两大自定义类型就学完了。

需要我出一道综合练习题(结构体+枚举+Option)吗?

Rust综合实战练习题(结构体 + 枚举 + Option + Vec + 方法 + match)

题目:图书订单管理系统

需求说明

  1. 定义枚举 OrderStatus ,订单状态,包含3个变体:
  • Pending :待付款(无附加数据)
  • Paid :已付款(存储付款时间字符串,例: "2026‑09‑05" )
  • Cancelled :已取消(存储取消原因: String )
  1. 定义结构体 Book (复用前面练习版本)
rust 复制代码
  
Book {
    title: String,
    price: f64,
}

实现构造方法 new(title:String, price:f64) -> Self

  1. 定义订单结构体 Order
  • id: u32 (订单编号)
  • book: Book (购买的图书)
  • quantity: u32 (购买数量)
  • status: OrderStatus(订单状态)

为 Order 实现3个方法:

① total_price(&self) -> f64 :订单总价 = 图书单价 × 购买数量

② pay(&mut self, pay_time: String) :

  • 如果当前状态是 Pending ,修改状态为 Paid(pay_time)
  • 如果已经是 Paid / Cancelled,不做任何操作
    ③ cancel(&mut self, reason: String) -> Option
  • 当前状态为 Pending:修改状态为 Cancelled(reason),返回 None (取消成功)
  • 已经付款:取消失败,返回 Some(format!("订单{}已付款,无法取消",self.id))
  1. main函数逻辑
  • 创建一个 Vec 订单列表,初始化两条订单:
    订单1:id=1,book:《Rust入门》,价格59.0,数量=2,状态Pending
    订单2:id=2,book:《算法教程》,价格45.0,数量=1,状态Paid("2026‑09‑01")
  • 调用订单1的pay方法,传入付款时间:"2026‑09‑05"
  • 调用订单2的cancel方法,传入原因:"用户临时不需要了";用match处理返回值,成功无输出,失败打印返回的错误消息。
  • 遍历全部订单,编写一个print_order函数(或者直接循环内match)打印订单详情:
    输出样例:

plaintext

订单#1:Rust入门 ×2,总价=118,状态:已付款(2026‑09‑05)

订单#2:算法教程 ×1,总价=45,状态:已付款(2026‑09‑01)

提示清单

  1. match匹配枚举分支,修改枚举状态的时候注意所有权;
  2. pay、cancel方法需要 &mut self,因为要修改订单;
  3. cancel 返回Option,取消成功返回None,失败返回Some(错误字符串);
  4. 无数据的枚举变体直接写 OrderStatus::Pending;
  5. 打印订单的时候,match匹配OrderStatus三个分支,输出不同文字。

动手写

先自己动手写,写完再对照参考答案👉点击展开完整参考答案

rust 复制代码
  
// 订单状态枚举
#[derive(Debug)]
enum OrderStatus {
    Pending,
    Paid(String),
    Cancelled(String),
}

// 图书结构体
#[derive(Debug)]
struct Book {
    title: String,
    price: f64,
}

impl Book {
    fn new(title: String, price: f64) -> Self {
        Book { title, price }
    }
}

// 订单结构体
#[derive(Debug)]
struct Order {
    id: u32,
    book: Book,
    quantity: u32,
    status: OrderStatus,
}

impl Order {
    // 订单总价
    fn total_price(&self) -> f64 {
        self.book.price * (self.quantity as f64)
    }

    // 付款操作
    fn pay(&mut self, pay_time: String) {
        if let OrderStatus::Pending = self.status {
            self.status = OrderStatus::Paid(pay_time);
        }
    }

    // 取消订单,返回Option,Some代表失败信息,None成功
    fn cancel(&mut self, reason: String) -> Option<String> {
        match &self.status {
            OrderStatus::Pending => {
                self.status = OrderStatus::Cancelled(reason);
                None
            }
            OrderStatus::Paid(_) | OrderStatus::Cancelled(_) => {
                Some(format!("订单{}已付款,无法取消", self.id))
            }
        }
    }
}

fn main() {
    // 创建订单集合
    let mut orders = vec![
        Order {
            id: 1,
            book: Book::new(String::from("Rust入门"), 59.0),
            quantity: 2,
            status: OrderStatus::Pending,
        },
        Order {
            id: 2,
            book: Book::new(String::from("算法教程"), 45.0),
            quantity: 1,
            status: OrderStatus::Paid(String::from("2026‑09‑01")),
        },
    ];

    // 订单1付款
    orders[0].pay(String::from("2026‑09‑05"));

    // 订单2尝试取消,match处理返回结果
    let res = orders[1].cancel(String::from("用户临时不需要了"));
    match res {
        Some(msg) => println!("{}", msg),
        None => (),
    }

    // 遍历打印订单
    for order in &orders {
        let status_text = match &order.status {
            OrderStatus::Pending => "待付款".to_string(),
            OrderStatus::Paid(t) => format!("已付款({})", t),
            OrderStatus::Cancelled(r) => format!("已取消({})", r),
        };
        println!(
            "订单#{}:{} ×{},总价={},状态:{}",
            order.id,
            order.book.title,
            order.quantity,
            order.total_price(),
            status_text
        );
    }
}
 

运行输出

plaintext

订单2已付款,无法取消

订单#1:Rust入门 ×2,总价=118,状态:已付款(2026‑09‑05)

订单#2:算法教程 ×1,总价=45,状态:已付款(2026‑09‑01)

关键知识点复盘

  1. if let :只关心枚举某一个分支的简化匹配语法,比完整match简洁;
  2. 模式匹配多分支 | : Paid()|Cancelled() 一条匹配多个变体;
  3. Option作为函数返回值用来表示操作成功/失败,不用panic;
  4. 结构体嵌套(Order里面包含Book),真实业务开发非常常见;
  5. 集合Vec存储多个业务对象,循环遍历处理;
  6. to_string() / format! 构造动态字符串。
    做完这道,Rust入门基础语法(变量、循环、函数、元组、数组、Vec、HashMap、结构体、枚举、Option、Result、模式匹配)全部通关。
    接下来就可以进入 所有权、借用、生命周期 这一Rust最核心、最难的模块,需要开启所有权专项练习吗?
相关推荐
zhanghaha13141 小时前
Python进阶教程:28_queue 队列模块 零基础超详细教程
java·开发语言·python
司小豆1 小时前
第七课:DeepSeek Harness 服务与依赖注入
java·服务器·开发语言·github·ai编程
weixin199701080161 小时前
[特殊字符]《闲鱼 + 淘宝 + 1688 三平台库存同源:二手ERP主数据治理与超卖防御》(附Python源码)
开发语言·python
ltqshs1 小时前
C语言-链表例程
c语言·开发语言·链表
小灰灰搞电子1 小时前
Rust+Slint 实现点击水波纹扩散效果按钮源码分享
rust·slint·水波纹按钮
shmily麻瓜小菜鸡1 小时前
JavaScript / TypeScript 易踩坑知识点 —— 作用域与变量类
开发语言·javascript·typescript
YYYing.1 小时前
【C++进阶系列 (一)】关于线程堆栈的那些事 (上篇)
c语言·开发语言·c++·线程堆栈
zh73141 小时前
Go 1.23 → 1.26 升级检查文档
开发语言·chrome·golang