ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

100-exercises-to-learn-rust 第 07 关:`Result` 的展开艺术——`unwrap`、`expect` 与 `match` 三选一

100-exercises-to-learn-rust 第 07 关:`Result` 的展开艺术——`unwrap`、`expect` 与 `match` 三选一 示例工程教程【免费下载链接】100-exercises-to-learn-rustA self-paced course to learn Rust, one exercise at a time.项目地址https://gitcode.com/GitHub_Trending/10/100-exercises-to-learn-rust点击查看免费下载导读在上一节06_fallibility中Ticket::new不再 panic而是返回ResultTicket, String把错误的处理权交还给了调用方。但把错误交还回来之后调用方到底该怎么接住它这正是本节07_unwrap要解决的问题。读完本文你将掌握 Rust 中消费Result的三种核心方式——unwrap、expect与match并能结合 07_unwrap 练习 中easy_ticket的实战场景理解何时该 panic、何时该优雅降级的取舍。背景Ticket::new变成了Result在 06_fallibility 中Ticket::new的签名从返回Ticket变成了返回ResultTicket, String一旦校验失败不再panic!而是构造并返回一个Err。这一改动彻底改变了调用方的处境——调用方再也不能假装失败不会发生。从练习源码 07_unwrap/src/lib.rs 可以看到Ticket::new的真实校验逻辑impl Ticket { pub fn new(title: String, description: String, status: Status) - ResultTicket, String { if title.is_empty() { return Err(Title cannot be empty.to_string()); } if title.len() 50 { return Err(Title cannot be longer than 50 bytes.to_string()); } if description.is_empty() { return Err(Description cannot be empty.to_string()); } if description.len() 500 { return Err(Description cannot be longer than 500 bytes.to_string()); } Ok(Ticket { title, description, status }) } }现在问题来了拿到这个Result之后调用方下一步该做什么失败无法被隐式地忽略这是 Rust 错误处理与异常机制最本质的区别不同于异常Result强制你在调用点处理错误。如果你调用一个返回Result的函数Rust 编译器不会允许你隐式地忽略错误分支。原文档给出了一个反例fn parse_int(s: str) - Resulti32, ParseIntError { // ... } // 这段代码无法编译我们没有处理错误分支。 // 我们必须使用 match 或 Result 提供的某个 combinator // 来展开成功值或处理错误。 let number parse_int(42) 2;为什么let number parse_int(42) 2;编译不过因为parse_int返回的不是i32而是Resulti32, ParseIntError对它做 2运算在类型上就是非法的。编译器会直接报错迫使你面对这样一个事实这个函数可能会失败你还没有想好失败时该怎么办。对比一下 06_fallibility 中关于异常机制的讨论在 Python、C# 等语言里仅看函数签名你根本不知道它会不会抛异常、会抛哪种异常抛异常的位置与捕获异常的位置之间隔着千山万水逻辑局部性很差。而 Rust 把可能失败这一信息编码进了类型系统——只要签名里出现Result调用方就一目了然。这也呼应了本教程的核心方法论fallibility易错性必须是显式的。panic!依然存在但它面向的是不可恢复的错误应当谨慎使用。你拿到了一个Result。现在怎么办原文档给出两种关键选择panic或显式解构。选择一失败即 panic——unwrap与expect如果你确信操作不会失败或者失败本身就是一个无法恢复的程序错误可以调用Result的两个方法// 如果 parse_int 返回 Err这里会 panic。 let number parse_int(42).unwrap(); // expect 允许你指定自定义的 panic 消息。 let number parse_int(42).expect(Failed to parse integer);两者行为上的区别只有一个panic 时携带的错误信息。unwrap()的 panic 消息是Err值自身的Debug输出expect(msg)则会把msg作为 panic 消息前缀便于后续排查。实践中更推荐expect——当几个月后 panic 真的发生时一句Failed to parse integer远比一堆裸数据更能帮你定位问题。注意unwrap和expect会把Ok(T)里的T直接解出来同时丢弃Err(E)分支的所有处理余地。它们本质上是把Result变回要么成功、要么崩溃的二元世界所以只应在失败等同于程序 bug的场合使用。选择二用match显式处理错误分支如果你希望针对失败做出有意义的响应打印日志、使用默认值、返回另一条错误等就应当用match把Result解构开来match parse_int(42) { Ok(number) println!(Parsed number: {}, number), Err(err) eprintln!(Error: {}, err), }match是穷尽的Result只有Ok和Err两个变体编译器会要求你两个分支都覆盖到。这种穷尽性正是 Rust 错误处理可靠性的来源——你永远不会忘了写else。关于match的展开细节可以回顾本教程前面的章节在 03_variants_with_data 中你已经学会了用模式匹配解构带数据的变体如Status::InProgress { assigned_to }而 04_if_let 则展示了只关心单个变体时用if let/let-else收窄分支的写法。Result的Ok/Err分支同样可以配合这些模式匹配语法使用。实战演练easy_ticket中的三种处理方式理解了unwrap/expect/match的分工后让我们看看它们如何在 07_unwrap 练习 中组合使用。练习要求实现easy_ticket// easy_ticket 在标题非法时应 panic // 而当描述非法时则改用默认描述Description not provided。 fn easy_ticket(title: String, description: String, status: Status) - Ticket { todo!() }这是一个非常典型的混合策略场景恰好把本文的两种选择都用上了标题校验失败 → panic。标题是票据的核心标识为空或超长意味着调用方传入了根本无效的数据这属于不可恢复的程序错误直接用expect展开即可。练习的测试用#[should_panic(expected Title cannot be empty)]与#[should_panic(expected Title cannot be longer than 50 bytes)]明确验证了这一点见 lib.rs 测试模块。注意这里的expected字符串与Ticket::new返回的Err消息完全对应——这正是原文档 08_error_enums 所警示的字符串匹配脆弱性一旦错误消息被同事改写测试和调用代码都会跟着断掉。这也预告了后续章节会引入错误枚举来消除这种耦合。描述校验失败 → 优雅降级。描述是可选的补充信息缺失时不应该让程序崩溃而是用默认文案兜底。这里就需要对Err分支做出反应而不是简单 panic。一个符合要求的实现大致长这样伪代码示意fn easy_ticket(title: String, description: String, status: Status) - Ticket { match Ticket::new(title, description, status) { Ok(ticket) ticket, Err(e) if e.contains(Title) panic!({}, e), // 标题错误panic Err(_) Ticket::new( fallback title.to_string(), Description not provided.to_string(), status, ).expect(fallback should always be valid), // 描述错误用默认描述重建 } }更简洁的写法是先用Ticket::new对标题做一次expect再对含描述的分支用match或unwrap_or_else兜底。无论哪种写法核心思路一致——把Result的两种变体分别路由到panic和恢复两条路径上这正对应原文档给出的两个选项。练习中template_description_is_used_if_empty与template_description_is_used_if_too_long两个测试见 lib.rs 测试模块验证了降级行为传入空描述或超长描述时得到的票据描述都应是Description not provided。测试数据如overly_long_description、valid_title来自 helpers/common 测试辅助 crate这也体现了本教程用独立 helper crate 复用测试数据的组织方式。如何运行与验证该练习是一个独立的 Rust crate位于 exercises/05_ticket_v2/07_unwrap/。其 Cargo.toml 声明了edition 2021并仅在 dev-dependencies 中引入commonhelper crate。在仓库根目录下进入该目录后执行cargo test即可看到上述 4 个测试全部通过两个#[should_panic]测试验证标题错误触发 panic且 panic 消息精确匹配两个普通测试验证描述非法时自动替换为默认文案。动手把easy_ticket的todo!()补全再跑一遍测试你就能直观感受到unwrap/expect/match三者各司其职的完整闭环。小结Result是值不是异常失败被编码进函数签名调用点必须显式处理否则无法编译。unwrap/expect把失败转化为 panic适合失败即程序 bug的场景需要自定义 panic 信息时优先expect。match以及if let/let-else对Ok/Err分支分别做出响应适合需要恢复、降级或传播错误的场景。实战取舍easy_ticket示范了同一次调用中标题失败就 panic、描述失败就降级的混合策略——选择哪种展开方式取决于失败本身是否可恢复。下一步08_error_enums 将解决用字符串当错误类型的脆弱性引入错误枚举把错误案例编码进类型系统——那正是unwrap与match得以优雅共存的关键基础设施。赞分享示例工程教程【免费下载链接】100-exercises-to-learn-rustA self-paced course to learn Rust, one exercise at a time.项目地址https://gitcode.com/GitHub_Trending/10/100-exercises-to-learn-rust点击查看免费下载相关推荐Linux Housekeeping 机制全解析CPU 隔离、RCU 同步与 cpumask 管理Linux Housekeeping 机制全解析CPU 隔离、RCU 同步与 cpumask 管理 Housekeeping 是 Linux 内核 CPU 隔示例工程教程Google Workspace CLI 事件订阅实战指南基于 gws-events 的订阅创建、事件流式接收与自动续期Google Workspace CLI 事件订阅实战指南基于 gws events 的订阅创建、事件流式接收与自动续期 导读 skills/gws even示例工程教程100-exercises-to-learn-rust 第 7 章用 Rust 无畏并发重构多线程 Ticket Store100 exercises to learn rust 第 7 章用 Rust 无畏并发重构多线程 Ticket Store 导读 本章是《100 exerc示例工程教程上一篇零基础掌握喜马拉雅音频下载工具从安装到精通的完整指南下一篇如何用Starless生成逼真黑洞图像完整安装与快速入门指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表