ARTICLE DETAIL

资讯详情

深耕网站SEO优化与搜索引擎排名提升的一线实战洞察。

如何用throw!宏让Rust错误处理更优雅:8个实用场景完整示例

如何用throw!宏让Rust错误处理更优雅:8个实用场景完整示例 如何用throw!宏让Rust错误处理更优雅8个实用场景完整示例【免费下载链接】fehlerRust doesnt have exceptions项目地址: https://gitcode.com/gh_mirrors/fe/fehlerRust 错误处理一直让不少初学者头疼每个可能失败的函数都要返回Result每个成功返回值还得手动包一层Ok。而 fehler 是一个专为 Rust 错误处理设计的库它通过throw!宏和#[throws]过程宏让可失败函数写起来几乎和不会失败的函数一样自然。本文用 8 个实用场景和完整示例手把手带你让 Rust 错误处理代码更优雅、更简洁。为什么Rust错误处理总显得啰嗦先看看没有 fehler 时的典型写法fn foo(x: bool) - Resulti32, i32 { if x { Ok(0) } else { Err(1) } }逻辑明明只有返回 0和出错返回 1代码却被Ok/Err包裹得看不清主线。函数越长、分支越多这种样板代码越干扰阅读。fehler 的思路是成功路径就正常写return失败路径用throw!宏抛出错误。同样的逻辑可以这样写use fehler::{throw, throws}; #[throws(i32)] fn foo(x: bool) - i32 { if x { 0 } else { throw!(1); } }调用方拿到的仍然是标准的Resulti32, i32运行时行为完全一致只是源码清爽了很多。快速上手安装 fehler 并跑通第一个示例在项目Cargo.toml中加入依赖即可[dependencies] fehler 1.0如果想从源码研究它的过程宏实现可以直接克隆仓库git clone https://gitcode.com/gh_mirrors/fe/fehler核心 API 只有两个#[throws]属性宏声明函数会抛错成功后无需Ok包裹throw!宏等价于Err($err)?用来主动抛出错误。两者的定义分别位于 src/lib.rs 和 fehler-macros/src/lib.rs。下面进入 8 个实用场景。场景一基础抛出告别 Ok 样板最简单的用法给函数加#[throws(错误类型)]成功路径直接返回值失败路径用throw!(错误值)。use fehler::{throw, throws}; #[throws(i32)] fn check(x: bool) - i32 { if x { 0 } else { throw!(1); } }check(true)得到Ok(0)check(false)得到Err(1)——和手写Result完全等价见 tests/throws.rs 中的对应用例。⚠️ 小提醒在#[throws]函数内部不能直接return Err(...)返回错误必须走throw!或?这是这套语法的唯一规矩。场景二指定自定义错误类型#[throws]的参数就是该函数的错误类型任何实现了From的类型都能用use fehler::{throw, throws}; use std::io; #[throws(io::Error)] fn load_config() - String { std::fs::read_to_string(config.toml)? }错误类型可以写任意路径比如std::io::Error、自己定义的枚举等写法自由。场景三定义默认错误类型一个参数都不用写如果整个模块都用同一种错误类型可以定义一个别名type Error之后直接写#[throws]或#[throws(_)]use fehler::{throw, throws}; type Error std::io::Error; // 本模块的默认错误类型 #[throws] fn read(name: str) - String { std::fs::read_to_string(name)? }这个默认错误类型特性在 src/lib.rs 的文档中有完整示例适合在业务模块内统一错误风格。场景四用 ? 串联失败调用错误一路透明传递#[throws]函数内部可以直接使用?把下层调用的错误原样向上传递无需任何map_erruse fehler::{throw, throws}; #[throws(io::Error)] fn first_line(path: str) - String { let text std::fs::read_to_string(path)?; // 读取失败直接透传 let line text.lines().next().unwrap_or(); if line.is_empty() { throw!(io::Error::from_raw_os_error(22)); // 内容不符合预期主动抛出 } line.to_string() }?负责转发已有错误throw!负责制造新错误二者配合覆盖了绝大多数错误处理需求。场景五async 函数照样能抛错#[throws]可以直接标注在async fn上配合await使用毫无障碍use fehler::{throw, throws}; #[throws(_)] async fn fetch_items(url: str) - i32 { // ... 发起网络请求失败则 ? 透传 ... 42 }tests/throws.rs 中提供了async_fn、async_fn_with_ret等测试用例覆盖无返回值和带返回值两种异步场景。场景六方法、泛型与结构体#[throws]不仅能用在自由函数上结构体方法和泛型函数同样适用use fehler::{throw, throws}; pub struct Counter { value: i32, } impl Counter { #[throws(i32)] pub fn bump(mut self) - i32 { if self.value i32::MAX { throw!(1); // 溢出时抛错 } self.value 1; self.value } }泛型错误类型也可以直接声明例如 tests/throws.rs 中的#[throws(E)] pub fn throws_genericsE() {}这让错误类型成为 API 的一部分由调用方决定。场景七用 Option 表达找不到静默失败更干净不是所有失败都值得构造一个错误。当函数找不到结果时返回None往往更合适。as Option语法就是为此而生use fehler::{throw, throws}; #[throws(as Option)] fn find(slice: [i32], needle: i32) - usize { if !slice.contains(needle) { throw!(); // 不带参数等价于返回 None } slice.iter().position(|x| x needle).unwrap() }find的签名自动变为Optionusize成功时是Some(下标)没找到时是None测试见 tests/option.rs。另外as语法还能配合结果别名如#[throws(as std::io::Result)]写法同样简洁。场景八main 函数与 no_std 环境最后两个进阶场景。其一main函数也能标注#[throws]避免手写Result返回use fehler::{throw, throws}; #[derive(Debug)] struct Error; #[throws(_)] fn do_it() - i32 { throw!(Error) } #[throws(_)] fn main() { do_it()?; }完整示例在 examples/throwing-main.rs。其二ehler 是no_std兼容的嵌入式等无标准库环境也能使用throw!和#[throws]对应测试在 tests/no_std.rs#![no_std] use fehler::{throw, throws}; #[throws(i32)] fn no_std_fn() { throw!(0); }总结一张表看懂 fehler 的核心用法写法效果#[throws(错误类型)]函数返回Result返回值, 错误类型成功无需Ok#[throws]/#[throws(_)]使用模块内type Error默认错误类型#[throws(as Option)]函数返回Optionthrow!()表示Nonethrow!(err)主动抛出错误等价于Err(err)??在 throws 函数内透传下层错误一句话总结fehler 没有改变 Rust 的错误模型只是把Ok样板从成功路径上拿走了。成功路径写得像普通函数失败路径用throw!和?表达——这就是 Rust 错误处理更优雅的打开方式。8 个场景掌握后你可以放心把它用在业务代码、异步服务和嵌入式项目里。【免费下载链接】fehlerRust doesnt have exceptions项目地址: https://gitcode.com/gh_mirrors/fe/fehler创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表