在 Rust 中,new()
方法通常用作构造函数,其错误处理需遵循 显式错误传递 原则(而非抛出异常)。以下是 3 种主要方案及示例:
方案 1:返回 Result<T, E>
(推荐)
通过 Result
封装成功值或错误,调用方需用 ?
或 match
处理。
use std::error::Error;
use std::fmt;
#[derive(Debug)]
struct User {
id: u32,
email: String,
}
#[derive(Debug)]
struct ValidationError(String);
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Invalid email: {}", self.0)
}
}
impl Error for ValidationError {} // 实现 Error trait
impl User {
// 返回 Result 以包含两种可能
pub fn new(id: u32, email: &str) -> Result<Self, ValidationError> {
if !email.contains('@') {
return Err(ValidationError(email.to_string()));
}
Ok(User { id, email: email.to_string() })
}
}
// 使用示例
fn main() -> Result<(), ValidationError> {
let user = User::new(1, "user@example.com")?; // 正确创建
let invalid_user = User::new(2, "invalid-email"); // 触发 Err(ValidationError)
Ok(())
}
方案 2:panic!
(仅限不可恢复错误)
仅在创建失败表示程序逻辑错误时使用(如违反不变式):
impl User {
pub fn new_strict(id: u32, email: &str) -> Self {
if email.is_empty() {
panic!("Email cannot be empty");
}
User { id, email: email.to_string() }
}
}
方案 3:返回 Option<T>
适用于**“有/无”场景**(不关心具体错误原因):
impl User {
pub fn new_optional(id: u32, email: &str) -> Option<Self> {
if email.contains('@') {
Some(User { id, email: email.to_string() })
} else {
None
}
}
}
最佳实践总结
场景 | 推荐方案 | 案例 |
---|---|---|
可恢复错误(如输入校验失败) | Result<T, E> |
用户输入邮箱格式错误 |
创建失败表示代码逻辑错误 | panic! |
初始化全局配置时读取到空文件 |
无需错误细节的简单检查 | Option<T> |
从缓存创建对象,缓存可能不存在 |
关键原则:
- 避免在
new
中隐藏错误(如返回默认值),除非是设计需求 - 优先实现
Error
trait 以支持错误传播(?
操作符)和链式错误 - 利用类型系统:通过参数类型(如
NonZeroU32
)在编译时避免部分错误
💡 进阶技巧:使用
thiserror
或anyhow
crate 简化错误处理:use thiserror::Error; #[derive(Error, Debug)] pub enum UserError { #[error("Invalid email format: {0}")] InvalidEmail(String), #[error("User ID overflow")] IdOverflow, } // 在 new 中直接返回 UserError::InvalidEmail(...)