知识点:自定义错误类型
use std::fmt;
// 方式1:用枚举定义错误类型
#[derive(Debug)]
enum AppError {
NotFound(String),
PermissionDenied(String),
ParseError(String),
NetworkError { url: String, code: u16 },
IoError(std::io::Error),
}
// 实现 Display(用户友好的错误信息)
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AppError::NotFound(msg) => write!(f, "未找到: {}", msg),
AppError::PermissionDenied(msg) => write!(f, "权限不足: {}", msg),
AppError::ParseError(msg) => write!(f, "解析错误: {}", msg),
AppError::NetworkError { url, code } => {
write!(f, "网络错误: {} (状态码: {})", url, code)
}
AppError::IoError(e) => write!(f, "IO错误: {}", e),
}
}
}
// 实现 std::error::Error(标准错误 trait)
impl std::error::Error for AppError {
// source() 返回导致此错误的底层错误(可选)
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
AppError::IoError(e) => Some(e),
_ => None,
}
}
}
// 从其他错误类型转换
impl From<std::io::Error> for AppError {
fn from(err: std::io::Error) -> Self {
AppError::IoError(err)
}
}
impl From<std::num::ParseIntError> for AppError {
fn from(err: std::num::ParseIntError) -> Self {
AppError::ParseError(err.to_string())
}
}
// 使用自定义错误
fn read_config(path: &str) -> Result<String, AppError> {
if !path.ends_with(".conf") {
return Err(AppError::ParseError("必须是 .conf 文件".into()));
}
// ? 会自动调用 From::from 转换错误类型
let content = std::fs::read_to_string(path)?;
Ok(content)
}
fn parse_port(s: &str) -> Result<u16, AppError> {
let port: u16 = s.parse()?; // ParseIntError -> AppError
if port < 1024 {
return Err(AppError::PermissionDenied(
format!("端口 {} 需要 root 权限", port)
));
}
Ok(port)
}
fn main() {
// 测试错误
match parse_port("abc") {
Ok(port) => println!("端口: {}", port),
Err(e) => println!("错误: {}", e),
}
match parse_port("80") {
Ok(port) => println!("端口: {}", port),
Err(e) => println!("错误: {}", e),
}
match parse_port("8080") {
Ok(port) => println!("端口: {}", port),
Err(e) => println!("错误: {}", e),
}
// 错误链
match read_config("test.txt") {
Ok(content) => println!("配置: {}", content),
Err(e) => {
println!("错误: {}", e);
// 打印错误链
let mut source = e.source();
while let Some(cause) = source {
println!(" 原因: {}", cause);
source = cause.source();
}
}
}
}
知识点:thiserror — 声明式错误定义
Cargo.toml:
[dependencies]
thiserror = "1"
main.rs
use thiserror::Error;
// 用 derive 宏大幅简化错误类型定义
#[derive(Error, Debug)]
enum DatabaseError {
#[error("连接失败: {host}:{port}")]
ConnectionFailed { host: String, port: u16 },
#[error("查询错误: {0}")]
QueryError(String),
#[error("记录不存在: id={0}")]
NotFound(i64),
// 自动实现 From<io::Error>
#[error("IO 错误")]
Io(#[from] std::io::Error),
// 自动实现 From<ParseIntError>
#[error("解析错误")]
Parse(#[from] std::num::ParseIntError),
// 透传底层错误信息
#[error("序列化失败")]
Serialization(#[source] serde_json::Error),
}
// 等价于手动实现:
// – Display(由 #[error("…")] 生成)
// – std::error::Error(自动实现)
// – From<std::io::Error>(由 #[from] 生成)
// – From<std::num::ParseIntError>(由 #[from] 生成)
// 结构化错误(带上下文字段)
#[derive(Error, Debug)]
#[error("用户 '{username}' 操作失败: {operation}")]
struct UserActionError {
username: String,
operation: String,
#[source]
cause: std::io::Error,
}
fn connect_db(host: &str, port: u16) -> Result<(), DatabaseError> {
if host.is_empty() {
return Err(DatabaseError::ConnectionFailed {
host: host.to_string(),
port,
});
}
// 模拟 IO 错误自动转换
let _file = std::fs::File::open("nonexistent")?; // io::Error -> DatabaseError
Ok(())
}
fn main() {
match connect_db("", 5432) {
Ok(()) => println!("连接成功"),
Err(e) => println!("错误: {}", e),
}
// 输出: 错误: 连接失败: :5432
match connect_db("localhost", 5432) {
Ok(()) => println!("连接成功"),
Err(e) => println!("错误: {}", e),
}
// 输出: 错误: IO 错误(自动从 io::Error 转换)
}
知识点:anyhow — 应用级错误处理
Cargo.toml:
[dependencies]
anyhow = "1"
main.rs
use anyhow::{Context, Result, bail, ensure};
// anyhow::Result<T> 等价于 Result<T, anyhow::Error>
// anyhow::Error 可以容纳任何实现了 std::error::Error 的错误
// 适合应用程序(不适合库)
fn read_config(path: &str) -> Result<String> {
// .context() 给错误添加上下文信息
let content = std::fs::read_to_string(path)
.with_context(|| format!("无法读取配置文件: {}", path))?;
Ok(content)
}
fn parse_port(line: &str) -> Result<u16> {
let parts: Vec<&str> = line.split('=').collect();
// ensure! 类似 assert!,但返回 Err 而非 panic
ensure!(parts.len() == 2, "配置格式错误,期望 key=value");
let port: u16 = parts[1].trim().parse()
.with_context(|| format!("端口值无效: '{}'", parts[1]))?;
// bail! 类似 return Err(…),提前返回错误
if port < 1024 {
bail!("端口 {} 需要 root 权限", port);
}
Ok(port)
}
// 链式上下文:层层添加信息
fn load_app_config() -> Result<Config> {
let path = std::env::var("CONFIG_PATH")
.unwrap_or_else(|_| "config.conf".to_string());
let content = read_config(&path)
.context("加载应用配置失败")?;
let port_line = content.lines()
.find(|l| l.starts_with("port"))
.context("配置中缺少 port 字段")?;
let port = parse_port(port_line)
.context("解析端口配置失败")?;
Ok(Config { port })
}
struct Config {
port: u16,
}
fn main() {
// 打印带完整上下文的错误
match load_app_config() {
Ok(config) => println!("端口: {}", config.port),
Err(e) => {
// {:?} 打印完整错误链
println!("错误详情:n{:?}", e);
// 也可以用 chain() 遍历错误链
println!("n错误链:");
for (i, cause) in e.chain().enumerate() {
println!(" {}: {}", i, cause);
}
}
}
}
知识点:thiserror vs anyhow 选择
规则:
库(library):用 thiserror 定义具体错误类型(让调用者能 match)
应用(application):用 anyhow 简化错误处理(不需要 match 具体类型)
=== 库的错误设计 ===
库应该暴露具体的错误枚举,让使用者能根据不同错误做不同处理
my_lib/src/error.rs
use thiserror::Error;
#[derive(Error, Debug)]
pub enum MyLibError {
#[error("HTTP 请求失败: {status}")]
HttpError { status: u16 },
#[error("JSON 解析失败: {0}")]
JsonError(#[from] serde_json::Error),
#[error("IO 错误: {0}")]
IoError(#[from] std::io::Error),
}
// 库的公开 API
fn fetch_user(id: u32) -> Result<String, MyLibError> {
if id == 0 {
return Err(MyLibError::HttpError { status: 404 });
}
Ok(format!("User_{}", id))
}
// === 应用的错误处理 ===
// 应用可以用 anyhow 统一处理所有错误
fn main() {
// 调用库时可以根据具体错误类型做不同处理
match fetch_user(0) {
Ok(user) => println!("用户: {}", user),
Err(MyLibError::HttpError { status }) => {
println!("HTTP 错误,状态码: {},尝试重试", status);
}
Err(MyLibError::JsonError(e)) => {
println!("数据格式异常: {}", e);
}
Err(e) => {
println!("其他错误: {}", e);
}
}
// 这就是为什么库要用具体错误类型:
// 调用者可以 match 不同变体,做不同的恢复操作
}
知识点:错误传播模式
use std::fmt;
#[derive(Debug)]
enum AppError {
Auth(String),
Database(String),
Validation(String),
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AppError::Auth(s) => write!(f, "认证错误: {}", s),
AppError::Database(s) => write!(f, "数据库错误: {}", s),
AppError::Validation(s) => write!(f, "验证错误: {}", s),
}
}
}
impl std::error::Error for AppError {}
// 模式1:? 运算符自动传播
fn get_user(id: u32) -> Result<String, AppError> {
if id == 0 {
return Err(AppError::Validation("ID 不能为 0".into()));
}
Ok(format!("User_{}", id))
}
fn get_user_orders(id: u32) -> Result<Vec<String>, AppError> {
let _user = get_user(id)?; // 错误自动传播
Ok(vec!["订单1".into(), "订单2".into()])
}
// 模式2:map_err 转换错误
fn parse_id(s: &str) -> Result<u32, AppError> {
s.parse::<u32>()
.map_err(|e| AppError::Validation(format!("无效ID '{}': {}", s, e)))
}
// 模式3:unwrap_or / unwrap_or_else 提供默认值
fn safe_divide(a: f64, b: f64) -> f64 {
if b == 0.0 {
0.0 // 默认值
} else {
a / b
}
}
// 模式4:ok() / err() 互转
fn try_parse(s: &str) {
let result: Result<i32, _> = s.parse();
// Result -> Option
let option = result.ok(); // Err 变 None
println!("parse('{}') => {:?}", s, option);
// Option -> Result
let result2 = option.ok_or(AppError::Validation("解析失败".into()));
println!("转换回 Result: {:?}", result2.is_ok());
}
// 模式5:在迭代器中处理错误
fn process_all(inputs: &[&str]) -> Vec<Result<u32, AppError>> {
inputs.iter()
.map(|s| parse_id(s))
.collect()
}
// 模式6:收集所有错误(不提前返回)
fn process_all_collect_errors(inputs: &[&str]) -> (Vec<u32>, Vec<AppError>) {
let mut values = vec![];
let mut errors = vec![];
for s in inputs {
match parse_id(s) {
Ok(v) => values.push(v),
Err(e) => errors.push(e),
}
}
(values, errors)
}
fn main() {
// 模式1
match get_user_orders(1) {
Ok(orders) => println!("订单: {:?}", orders),
Err(e) => println!("错误: {}", e),
}
// 模式2
match parse_id("abc") {
Ok(id) => println!("ID: {}", id),
Err(e) => println!("错误: {}", e),
}
// 模式3
println!("10/3 = {}", safe_divide(10.0, 3.0));
println!("10/0 = {}", safe_divide(10.0, 0.0));
// 模式4
try_parse("42");
try_parse("abc");
// 模式5
let results = process_all(&["1", "abc", "3", "def"]);
for r in &results {
match r {
Ok(v) => println!("成功: {}", v),
Err(e) => println!("失败: {}", e),
}
}
// 模式6
let (values, errors) = process_all_collect_errors(&["1", "abc", "3", "def"]);
println!("成功值: {:?}", values);
println!("错误数: {}", errors.len());
}
知识点:Result 的组合方法
fn main() {
// and_then:链式操作(前一步成功才执行下一步)
let result: Result<i32, String> = Ok(5);
let chained = result
.and_then(|v| {
if v > 0 { Ok(v * 2) }
else { Err("负数".into()) }
})
.and_then(|v| {
if v < 100 { Ok(v + 1) }
else { Err("太大".into()) }
});
println!("链式: {:?}", chained); // Ok(11)
// map_err:转换错误类型
let r: Result<i32, &str> = Err("原始错误");
let r2: Result<i32, String> = r.map_err(|e| format!("包装: {}", e));
println!("map_err: {:?}", r2);
// or / or_else:提供备选 Result
let r1: Result<i32, &str> = Err("失败");
let r2: Result<i32, &str> = Ok(42);
println!("or: {:?}", r1.or(r2)); // Ok(42)
// flatten:Result<Result<T, E>, E> -> Result<T, E>
let nested: Result<Result<i32, &str>, &str> = Ok(Ok(42));
let flat: Result<i32, &str> = nested.flatten();
println!("flatten: {:?}", flat);
// Option 的类似方法
let opt: Option<i32> = Some(5);
let doubled = opt
.and_then(|v| if v > 0 { Some(v * 2) } else { None })
.map(|v| v + 1);
println!("Option 链式: {:?}", doubled); // Some(11)
// Option 组合
let a: Option<i32> = Some(1);
let b: Option<i32> = Some(2);
let c: Option<i32> = None;
// zip:两个 Option 都有值时组成元组
println!("zip(Some, Some): {:?}", a.zip(b)); // Some((1, 2))
println!("zip(Some, None): {:?}", a.zip(c)); // None
// filter:条件过滤
println!("filter(>0): {:?}", a.filter(|&x| x > 0)); // Some(1)
println!("filter(>5): {:?}", a.filter(|&x| x > 5)); // None
}
知识点:never 类型 ! 与错误处理
// ! 表示"永远不会返回"的类型(never type)
// 它可以转换为任何类型,所以在 Result 中很有用
fn always_succeeds() -> Result<i32, !> {
// 这个函数永远不会失败
Ok(42)
}
// 在 match 中,! 可以匹配任何分支
fn example() {
let result: Result<i32, !> = Ok(42);
match result {
Ok(v) => println!("值: {}", v),
Err(_) => unreachable!("不可能发生"),
}
}
// todo!() / unimplemented!() / panic!() 的返回类型是 !
fn not_implemented() -> i32 {
todo!("稍后实现") // ! 可以当作 i32 返回
}
fn main() {
example();
// unwrap 的内部实现就利用了 !
// fn unwrap(self) -> T {
// match self {
// Ok(v) => v,
// Err(e) => panic!("…"), // panic! 返回 !,可以当作 T
// }
// }
}
知识点:实战 — 完整的错误处理架构
use std::fmt;
use std::num::ParseIntError;
// === 领域错误(具体、可匹配) ===
#[derive(Debug)]
enum UserError {
NotFound { id: u64 },
AlreadyExists { email: String },
InvalidEmail(String),
InvalidAge(String),
}
impl fmt::Display for UserError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UserError::NotFound { id } => write!(f, "用户不存在: id={}", id),
UserError::AlreadyExists { email } => write!(f, "邮箱已注册: {}", email),
UserError::InvalidEmail(e) => write!(f, "无效邮箱: {}", e),
UserError::InvalidAge(e) => write!(f, "无效年龄: {}", e),
}
}
}
impl std::error::Error for UserError {}
// === 领域模型 ===
#[derive(Debug)]
struct User {
id: u64,
email: String,
age: u32,
}
// === 验证函数 ===
fn validate_email(email: &str) -> Result<&str, UserError> {
if email.contains('@') && email.contains('.') {
Ok(email)
} else {
Err(UserError::InvalidEmail(email.to_string()))
}
}
fn validate_age(age_str: &str) -> Result<u32, UserError> {
let age: u32 = age_str.parse()
.map_err(|e: ParseIntError| UserError::InvalidAge(e.to_string()))?;
if age > 150 {
return Err(UserError::InvalidAge(format!("年龄 {} 不合理", age)));
}
Ok(age)
}
// === 业务逻辑 ===
struct UserService {
users: Vec<User>,
next_id: u64,
}
impl UserService {
fn new() -> Self {
UserService { users: vec![], next_id: 1 }
}
fn create_user(&mut self, email: &str, age_str: &str) -> Result<User, UserError> {
// 验证
let email = validate_email(email)?.to_string();
let age = validate_age(age_str)?;
// 业务规则
if self.users.iter().any(|u| u.email == email) {
return Err(UserError::AlreadyExists { email });
}
// 创建
let user = User {
id: self.next_id,
email,
age,
};
self.next_id += 1;
self.users.push(user);
Ok(User { id: self.next_id – 1, email: self.users.last().unwrap().email.clone(), age: self.users.last().unwrap().age })
}
fn find_user(&self, id: u64) -> Result<&User, UserError> {
self.users.iter()
.find(|u| u.id == id)
.ok_or(UserError::NotFound { id })
}
}
fn main() {
let mut service = UserService::new();
// 成功创建
match service.create_user("alice@example.com", "25") {
Ok(user) => println!("创建成功: {:?}", user),
Err(e) => println!("创建失败: {}", e),
}
// 无效邮箱
match service.create_user("invalid-email", "30") {
Ok(user) => println!("创建成功: {:?}", user),
Err(e) => println!("创建失败: {}", e),
}
// 无效年龄
match service.create_user("bob@example.com", "abc") {
Ok(user) => println!("创建成功: {:?}", user),
Err(e) => println!("创建失败: {}", e),
}
// 重复邮箱
match service.create_user("alice@example.com", "28") {
Ok(user) => println!("创建成功: {:?}", user),
Err(e) => println!("创建失败: {}", e),
}
// 查找用户
match service.find_user(1) {
Ok(user) => println!("找到用户: {:?}", user),
Err(e) => println!("查找失败: {}", e),
}
match service.find_user(999) {
Ok(user) => println!("找到用户: {:?}", user),
Err(e) => println!("查找失败: {}", e),
}
}
核心规则
概念 写法
自定义错误枚举 enum MyError { Variant1, Variant2(…) }
实现 Display impl fmt::Display for MyError
实现 Error impl std::error::Error for MyError
From 转换 impl From for MyError
? 运算符 let x = fallible_fn()?;(自动调用 From)
thiserror #[derive(Error)] #[error(“…”)]
anyhow anyhow::Result, .context(), bail!, ensure!
库用 thiserror 暴露具体错误类型,让调用者 match
应用用 anyhow 统一错误处理,添加上下文
map_err result.map_err( e MyError::from(e))
错误链 e.source() / e.chain()
动手试试
补全下面的代码:
use std::fmt;
use std::num::ParseIntError;
// 补全:定义错误枚举 CalculatorError,包含以下变体:
// 1. DivisionByZero
// 2. InvalidNumber(String) — 解析数字失败
// 3. UnknownOperator(String) — 未知运算符
// 4. Overflow(String) — 计算溢出
// 补全:为 CalculatorError 实现 Display
// DivisionByZero => "除数不能为零"
// InvalidNumber(s) => "无效数字: {s}"
// UnknownOperator(s) => "未知运算符: {s}"
// Overflow(s) => "计算溢出: {s}"
// 补全:为 CalculatorError 实现 std::error::Error
// 补全:从 ParseIntError 转换为 CalculatorError
// impl From<ParseIntError> for CalculatorError
// 补全:实现计算器函数
// 接受格式为 "操作数1 运算符 操作数2" 的字符串
// 支持 +, -, *, / 四种运算
// 所有运算在 i64 范围内进行
// 除法时检查除数为零
// 乘法时检查溢出(用 checked_mul)
fn calculate(expression: &str) -> Result<i64, CalculatorError> {
// 补全
// 提示:
// 1. 用 split_whitespace 分割
// 2. 解析操作数(用 ? 自动转换 ParseIntError)
// 3. match 运算符
// 4. 用 checked_add/checked_sub/checked_mul 防溢出
todo!()
}
// 补全:实现批量计算函数
// 接受多行表达式,返回每行的结果
// 如果某行出错,结果中放入错误信息字符串
fn calculate_batch(expressions: &str) -> Vec<String> {
// 补全
// 提示:逐行处理,Ok 转为 "结果: {value}",Err 转为 "错误: {error}"
todo!()
}
fn main() {
// 测试单个计算
let tests = vec![
"10 + 20",
"100 – 37",
"6 * 7",
"15 / 3",
"10 / 0",
"abc + 1",
"10 % 3",
"999999999999 * 999999999999",
];
for expr in &tests {
match calculate(expr) {
Ok(result) => println!("{} = {}", expr, result),
Err(e) => println!("{} => 错误: {}", expr, e),
}
}
// 期望:
// 10 + 20 = 30
// 100 – 37 = 63
// 6 * 7 = 42
// 15 / 3 = 5
// 10 / 0 => 错误: 除数不能为零
// abc + 1 => 错误: 无效数字: abc
// 10 % 3 => 错误: 未知运算符: %
// 999999999999 * 999999999999 => 错误: 计算溢出: …
println!("n— 批量计算 —");
// 测试批量计算
let batch = "10 + 20n100 / 0nabc * 2n6 * 7";
let results = calculate_batch(batch);
for r in &results {
println!("{}", r);
}
// 期望:
// 结果: 30
// 错误: 除数不能为零
// 错误: 无效数字: abc
// 结果: 42
}
期望输出:
10 + 20 = 30
100 – 37 = 63
6 * 7 = 42
15 / 3 = 5
10 / 0 => 错误: 除数不能为零
abc + 1 => 错误: 无效数字: abc
10 % 3 => 错误: 未知运算符: %
999999999999 * 999999999999 => 错误: 计算溢出: i64 multiplication overflow
— 批量计算 —
结果: 30
错误: 除数不能为零
错误: 无效数字: abc
结果: 42
写好了贴过来,没问题就进入第29课:智能指针进阶与内部可变性 👋
贴代码
完整代码实现
use std::error::Error;
use std::fmt;
use std::num::ParseIntError;
#[derive(Debug)]
enum CalculatorError {
DivisionByZero,
InvalidNumber(String),
UnknownOperator(String),
Overflow(String),
}
impl fmt::Display for CalculatorError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CalculatorError::DivisionByZero => write!(f, "除数不能为零"),
CalculatorError::InvalidNumber(s) => write!(f, "无效数字: {}", s),
CalculatorError::UnknownOperator(s) => write!(f, "未知运算符: {}", s),
CalculatorError::Overflow(s) => write!(f, "计算溢出: {}", s),
}
}
}
impl Error for CalculatorError {}
impl From<ParseIntError> for CalculatorError {
fn from(e: ParseIntError) -> Self {
CalculatorError::InvalidNumber(e.to_string())
}
}
fn calculate(expression: &str) -> Result<i64, CalculatorError> {
let parts: Vec<&str> = expression.split_whitespace().collect();
if parts.len() != 3 {
return Err(CalculatorError::UnknownOperator("表达式格式错误".to_string()));
}
let left: i64 = parts[0].parse()?;
let op = parts[1];
let right: i64 = parts[2].parse()?;
match op {
"+" => left
.checked_add(right)
.ok_or_else(|| CalculatorError::Overflow(format!("{} + {}", left, right))),
"-" => left
.checked_sub(right)
.ok_or_else(|| CalculatorError::Overflow(format!("{} – {}", left, right))),
"*" => left
.checked_mul(right)
.ok_or_else(|| CalculatorError::Overflow(format!("{} * {}", left, right))),
"/" => {
if right == 0 {
Err(CalculatorError::DivisionByZero)
} else {
// i64::checked_div 溢出只发生在 i64::MIN / -1
left.checked_div(right)
.ok_or_else(|| CalculatorError::Overflow(format!("{} / {}", left, right)))
}
}
op => Err(CalculatorError::UnknownOperator(op.to_string())),
}
}
fn calculate_batch(expressions: &str) -> Vec<String> {
let mut res = Vec::new();
for line in expressions.lines() {
match calculate(line) {
Ok(v) => res.push(format!("结果: {}", v)),
Err(e) => res.push(format!("错误: {}", e)),
}
}
res
}
fn main() {
// 测试单个计算
let tests = vec![
"10 + 20",
"100 – 37",
"6 * 7",
"15 / 3",
"10 / 0",
"abc + 1",
"10 % 3",
"999999999999 * 999999999999",
];
for expr in &tests {
match calculate(expr) {
Ok(result) => println!("{} = {}", expr, result),
Err(e) => println!("{} => 错误: {}", expr, e),
}
}
println!("\\n— 批量计算 —");
// 测试批量计算
let batch = "10 + 20\\n100 / 0\\nabc * 2\\n6 * 7";
let results = calculate_batch(batch);
for r in &results {
println!("{}", r);
}
}
运行输出
plaintext
10 + 20 = 30
100 – 37 = 63
6 * 7 = 42
15 / 3 = 5
10 / 0 => 错误: 除数不能为零
abc + 1 => 错误: 无效数字: invalid digit found in string
10 % 3 => 错误: 未知运算符: %
999999999999 * 999999999999 => 错误: 计算溢出: 999999999999 * 999999999999
— 批量计算 —
结果: 30
错误: 除数不能为零
错误: 无效数字: invalid digit found in string
结果: 42
知识点详解
1. 自定义错误枚举 CalculatorError
四个变体:DivisionByZero、InvalidNumber、UnknownOperator、Overflow。
2. Display trait
用于人类可读的错误提示, println!(“{}”, err) 会调用 Display。
3. Error trait
标准库错误trait;只要实现 Display + Debug ,直接写 impl Error for CalculatorError {} 即可,没有额外方法。
4. From
实现类型转换, parse()? 自动把解析整数错误转换成我们自定义的 CalculatorError,这是Rust错误链的经典写法。
5. calculate 函数关键点
a. split_whitespace() :按空白分割表达式,得到左右操作数和运算符;
b. parse()? :自动利用 From 把 ParseIntError 转为 CalculatorError;
c. checked_add / checked_sub / checked_mul / checked_div:返回 Option,溢出返回 None;
d. ok_or_else :把 Option 转成 Result,构造Overflow错误,使用闭包延迟生成错误字符串(性能更好);
e. 除法单独判断除数等于零,返回 DivisionByZero。
checked_div 的溢出场景: i64::MIN / -1 ,其余普通除法不会溢出。
f. calculate_batch
lines() 遍历每一行;Ok → “结果: xxx”;Err → “错误: xxx”,收集到Vec返回。




