【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最核心、最难的模块,需要开启所有权专项练习吗?
相关推荐
破茧蝴蝶1 小时前
Python分布式爬虫完整教程
开发语言·python
狗凯之家源码网2 小时前
全新 PHP 轻量客服聊天室源码深度评测
开发语言·php·聊天室系统
我是伪码农2 小时前
百蝶杯学习
学习
远离UE42 小时前
UE5 Substrate Thin Film 源码拆析
开发语言·javascript·ue5
keyipatience2 小时前
JavaScript学习(1)书写位置,变量、数据类型、运算符
开发语言·前端·javascript·css·学习
栈帧之外3 小时前
【C++】STL之set
开发语言·c++
catchadmin4 小时前
免费可商用 PHP 管理后台 CatchAdmin V5.4.1 发布,新增 MCP 支持
开发语言·php
xian_wwq4 小时前
【学习笔记】Milvus 如何完成一次向量相似度检索,Milvus 索引到底应该怎么选?-15
笔记·学习·milvus
Hhuhkt4 小时前
3D 云养宠小游戏
学习
jimy15 小时前
c++的 Type Traits (二、更详细版本)
开发语言·c++