目录
自定义错误类型示例
文件 src\lib.rs 内容如下:
use std::{env, fs, path::PathBuf};
use serde::Deserialize;
/// 应用配置
#[derive(Debug, Clone, Deserialize)]
pub struct AppConfig {
pub server: ServerConfig,
pub database: DatabaseConfig,
pub auth: AuthConfig,
}
/// 服务器配置
#[derive(Debug, Clone, Deserialize)]
pub struct ServerConfig {
pub addr: String,
}
/// 数据库配置
#[derive(Debug, Clone, Deserialize)]
pub struct DatabaseConfig {
pub url: String,
}
/// 认证配置
#[derive(Debug, Clone, Deserialize)]
pub struct AuthConfig {
pub jwt_secret: String,
}
/// 加载应用配置
impl AppConfig {
pub fn load() -> Result<Self, ConfigError> {
// 从环境变量中获取环境名称,如:dev/test/prod,默认值为 dev
let env_name = env::var("APP_ENV").unwrap_or_else(|_| "dev".to_string());
let path = PathBuf::from("config").join(format!("env_{env_name}.toml"));
let content = fs::read_to_string(&path).map_err(|source| ConfigError::Read {
path: path.clone(),
source,
})?;
let mut config: AppConfig =
toml::from_str(&content).map_err(|source| ConfigError::Parse {
path: path.clone(),
source,
})?;
if let Ok(addr) = env::var("FUYO_API_ADDR") {
config.server.addr = addr;
}
if let Ok(url) = env::var("DATABASE_URL") {
config.database.url = url;
}
if let Ok(secret) = env::var("FUYO_JWT_SECRET") {
config.auth.jwt_secret = secret;
}
config.validate()?;
Ok(config)
}
fn validate(&self) -> Result<(), ConfigError> {
if self.server.addr.trim().is_empty() {
return Err(ConfigError::Invalid("server.addr 不能为空"));
}
if self.database.url.trim().is_empty() {
return Err(ConfigError::Invalid("database.url 不能为空"));
}
if self.auth.jwt_secret.trim().is_empty() {
return Err(ConfigError::Invalid("auth.jwt_secret 不能为空"));
}
Ok(())
}
}
#[derive(Debug)]
pub enum ConfigError {
/// 读取配置文件失败
Read {
path: PathBuf,
source: std::io::Error,
},
/// 解析配置文件失败
Parse {
path: PathBuf,
source: toml::de::Error,
},
/// 配置错误
Invalid(&'static str),
}
impl std::fmt::Display for ConfigError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Read { path, source } => {
write!(f, "读取配置文件 {} 失败: {source}", path.display())
}
Self::Parse { path, source } => {
write!(f, "解析配置文件 {} 失败: {source}", path.display())
}
Self::Invalid(message) => write!(f, "配置错误: {message}"),
}
}
}
impl std::error::Error for ConfigError {}
AI提问:为什么是空实现:impl std::error::Error for ConfigError { } ?
AI回复:
我来查看这段代码的上下文,了解 ConfigError 的定义和实现细节。
代码分析
这段代码 impl std::error::Error for ConfigError {} 的作用是为自定义错误类型 ConfigError 实现 Rust 标准库的 Error trait。
为什么是空实现?
这是 Rust 1.41+ 的语言特性。在 Rust 1.41 之前,std::error::Error trait 需要实现一个必需的方法 description(&self) -> &str。但从 Rust 1.41 开始:
description()方法被标记为#[deprecated],并提供了默认实现(基于Displaytrait)- 所有
Errortrait 的方法都变成了可选的,都有默认实现
因此,只需要这样的空实现块,编译器就会自动为 ConfigError 生成所有 Error trait 方法的默认实现。
【重点】关键依赖关系
Error trait 的默认实现依赖于 ConfigError 已经实现了另外两个 trait:
| 必需 trait | 实现位置 | 作用 |
|---|---|---|
std::fmt::Debug |
第 75 行 #[derive(Debug)] |
支持调试输出 |
std::fmt::Display |
第 91-103 行 | 支持用户友好的错误消息格式化 |
这样做的好处
实现 std::error::Error trait 后,ConfigError 获得以下能力:
- 可以与
?操作符配合使用:在返回Result<T, impl std::error::Error>的函数中传播错误 - 支持错误链(Error Chain):