Day 7:第十週總結練習
學習目標
- 整合 thiserror / anyhow / tracing / 非同步
- 完成一個具備完整錯誤處理與日誌的程式
今日重點
本週回顧
- thiserror:精簡定義錯誤、#[from]
- anyhow:快速傳播、context
- 錯誤處理模式(recovery / chain / error as value)
- tracing:event / span / structure field
綜合練習:非同步 HTTP 下載器(含完整錯誤與日誌)
use anyhow::{Result, Context};
use serde::Deserialize;
use thiserror::Error;
use tracing::{info, error, warn, instrument};
#[derive(Error, Debug)]
enum AppError {
#[error("http request failed: {0}")]
Http(String),
#[error("json parse failed: {0}")]
Json(#[from] serde_json::Error),
}
#[derive(Deserialize, Debug)]
struct Todo {
id: u32,
title: String,
}
#[instrument]
async fn fetch_todo(id: u32) -> Result<Todo> {
let url = format!("https://jsonplaceholder.typicode.com/todos/{}", id);
info!(id, "fetching todo");
let body = reqwest::get(&url)
.await
.map_err(|e| AppError::Http(e.to_string()))
.context("request todo")?;
let todo = body.json::<Todo>().await?; // serde_json 自動轉 AppError::Json
Ok(todo)
}
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt().init();
for id in 1..=3 {
match fetch_todo(id).await {
Ok(todo) => info!("todo {}: {}", todo.id, todo.title),
Err(e) => error!("failed: {:?}", e), // anyhow 錯誤鏈
}
}
Ok(())
}
練習換成並行版本(spawn + join)並觀察 tracing span。
挑戰
- 加上
timeout讓請求限時。 - 用
#[instrument]觀察每個 span 的耗時。 - 思考 library vs application 的錯誤責任。
自我檢查
- 能整合 thiserror + anyhow + tracing + async
- 會設計內外層錯誤策略
- 能寫結構化、可診斷的日誌
- 具備生產等級的錯誤處理能力
週總結
至此你已有「生產級」的 Rust 工程能力。 下一週進入 12 週的最終寫照:實戰專案!