Skip to main content

handle-errors-in-code

Guide for error handling in this Rust project. Covers the four principles (clarity, context, actionability, explicit enums over anyhow), the thiserror pattern for structured errors, including what/where/when/why context, writing actionable help text, and avoiding vague errors. Also covers the located-error package for errors with source location. Use when writing error types, handling Results, adding error variants, or reviewing error messages. Triggers on "error handling", "error type", "Result", "thiserror", "anyhow", "error enum", "error message", "handle error", "add error variant", or "located-error".

来源信息

仓库
torrust/torrust-tracker
最近来源活动
2026年10月20日 20:55
检测到的 SKILL.md 语言
英语
星标
536
Forks
58

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
handle-errors-in-code
description
Guide for error handling in this Rust project. Covers the four principles (clarity, context, actionability, explicit enums over anyhow), the thiserror pattern for structured errors, including what/where/when/why context, writing actionable help text, and avoiding vague errors. Also covers the located-error package for errors with source location. Use when writing error types, handling Results, adding error variants, or reviewing error messages. Triggers on "error handling", "error type", "Result", "thiserror", "anyhow", "error enum", "error message", "handle error", "add error variant", or "located-error".
metadata
{"author":"torrust","version":"1.0"}
# Handling Errors in Code ## Core Principles 1. **Clarity** — Users immediately understand what went wrong 2. **Context** — Include what/where/when/why 3. **Actionability** — Tell users how to fix it 4. **Explicit enums over `anyhow`** — Prefer structured errors for pattern matching ## Prefer Explicit Enum Errors ```rust // ✅ Correct: explicit, matchable, clear #[derive(Debug, thiserror::Error)] pub enum TrackerError { #[error("Torrent '{info_hash}' not found in whitelist")] TorrentNotWhitelisted { info_hash: InfoHash }, #[error("Peer limit exceeded for torrent '{info_hash}': max {limit}")] PeerLimitExceeded { info_hash: InfoHash, limit: usize }, } // ❌ Wrong: opaque, hard to match return Err(anyhow::anyhow!("Something went wrong")); return Err("Invalid input".into()); ``` ## Include Actionable Fix Instructions in Display When the error is user-facing, add instructions: ```rust #[error( "Configuration file not found at '{path}'.\n\ Copy the default: cp share/default/config/tracker.toml {path}" )] ConfigNotFound { path: PathBuf }, ``` ## Context Requirements Each error should answer: - **What**: What operation was being performed? - **Where**: Which component, file, or resource? - **When**: Under what conditions? - **Why**: What caused the failure? ```rust // ✅ Good: full context #[error("UDP socket bind failed for '{addr}': {source}. Is port {port} already in use?")] SocketBindFailed { addr: SocketAddr, port: u16, source: std::io::Error }, // ❌ Bad: no context return Err("bind failed".into()); ``` ## The `located-error` Package For errors that benefit from source location tracking, use the `torrust-located-error` crate. It was extracted to [torrust/torrust-located-error](https://github.com/torrust/torrust-located-error) and is a crates.io dependency, not a workspace path: ```toml [dependencies] torrust-located-error = "3.0.0" ``` ```rust use torrust_located_error::Located; // Wraps any error with file and line information let err = Located(my_error).into(); ``` ## Unwrap and Expect Policy | Context | `.unwrap()` | `.expect("msg")` | `?` / `Result` | | ---------------------- | ----------- | ----------------------------------------- | -------------- | | Production code | Never | Only when failure is logically impossible | Default | | Tests and doc examples | Acceptable | Preferred when message adds clarity | — | ```rust // ✅ Production: propagate errors with ? fn load_config(path: &Path) -> Result<Config, ConfigError> { let content = std::fs::read_to_string(path) .map_err(|e| ConfigError::FileAccess { path: path.to_path_buf(), source: e })?; toml::from_str(&content) .map_err(|e| ConfigError::InvalidToml { path: path.to_path_buf(), source: e }) } // ✅ Tests: unwrap() is fine #[test] fn it_should_parse_valid_config() { let config = Config::parse(VALID_TOML).unwrap(); assert_eq!(config.http_api.bind_address, "127.0.0.1:1212"); } ``` ## When Public APIs Return `Result` Return `Result` only when an operation can fail today, crosses an I/O boundary, or is a trait or port with an existing or planned backend that can fail. Otherwise return a plain value; a real failure later is a semver-signalled breaking change. Never use `Infallible` or an empty error enum as a placeholder, and mark existing public error enums `#[non_exhaustive]` before first publish, except an enum whose variants select different recovery actions (stop or continue, reply or drop): keep it exhaustive and say why in a doc comment, so that a new variant is a deliberate breaking change. See [ADR 20261005145329](../../../../../docs/adrs/20261005145329_return_result_only_for_concretely_fallible_public_apis.md). ## Quick Checklist - [ ] Error type uses `thiserror::Error` derive - [ ] Error message includes specific context (names, paths, addresses, values) - [ ] Error message includes fix instructions where possible - [ ] Prefer `enum` over `Box<dyn Error>` or `anyhow` in library code - [ ] No vague messages like "invalid input" or "error occurred" - [ ] No `.unwrap()` in production code (tests and doc examples are fine) - [ ] Consider `located-error` for diagnostics-rich errors
在 GitHub 查看