文章
Rust 错误处理最佳实践
use anyhow::{Context, Result, bail, ensure};
fn fetch_today_updates(doc: &Document) -> Result<Vec> { let today_box = doc .select(&list_box_sel) .find(|n| …) .with_context(|| "未找到带 ‘今天’ 的更新列表节点")?;
let items = today_box
.select(&item_sel)
.map(|node| {
parse_item(node)
.with_context(|| format!("解析项失败: {:?}", node))
})
.collect::<Result<_, _>>()?;
Ok(items)
}
fn parse_count(text: &str) -> Result { let count: u32 = text.trim().parse() .with_context(|| format!("解析数字失败: '{}'", text))?; ensure!(count > 0, "数量必须 > 0,实际为 {}", count); Ok(count) }
fn main() { env_logger::init(); if let Err(err) = run() { eprintln!("错误: {:#}", err); std::process::exit(1); } }
fn run() -> Result<()> { let html = std::fs::read_to_string("updates.html") .context("读取 updates.html 失败")?; let doc = scraper::Html::parse_document(&html); let updates = fetch_today_updates(&doc)?; if updates.is_empty() { println!("今日暂无更新"); } else { updates.iter().for_each(|u| println!("- [{}] {}", u.time, u.title)); } Ok(()) }
---
### 6. 边界与日志
- **库(Library)**:暴露 `Result<T, E>`,让调用者决定如何处理。
- **应用(Application)**:在 `main` 或顶层统一捕获并打印错误。
- **日志**:使用 `log` / `env_logger` 打印警告或错误信息:
```rust
log::warn!("未找到今日列表,返回空结果");
```
---
### 总结
1. **极少 panic**:只有真正“绝不会发生”风险处使用。
2. **Option / Result**:通过 `if let`、`match`、 `?`、 组合子清晰处理。
3. **错误类型**:应用层用 `anyhow`,库用 `thiserror`。
4. **丰富上下文**:`.context()` 提供定位信息。
5. **统一捕获**:在应用入口层处理错误,输出给用户或退出。
6. **合理日志**:在降级场景下记录,辅助调试和监控。
# 📎 参考文章
- <mention-page url="https://app.notion.com/p/2345c571bb7d8053ae7ddbf9b574a559"/>
<empty-block/>