讲解
Rust 没有 null。这个价值数十亿美元的错误被枚举 Option 取代:Some(T) 表示有值,None 表示没有。关键点:Option 和 T 是不同的类型——手里有 Option 时编译器不允许你直接当 i32 用,必须先处理 None 的可能性才能拿到值。「可能不存在」从运行时空指针异常变成了编译期类型约束。
Result<T, E> 是同样的思想用于「可能失败」:Ok(T) 成功带结果,Err(E) 失败带原因。文件读取、网络请求、字符串解析这些会失败的操作,返回的都是 Result。和第 18 章的 panic 不同,Result 是给「预期内的、可恢复的失败」用的。
手工 match 之外,两者都有丰富的组合子让代码更顺:unwrap_or(default) 给默认值;map(f) 有值就变换;unwrap()/expect("原因") 直接取值、没有就 panic(仅在确定有值或示例代码中使用)。最重要的是 ? 运算符:在返回 Result(或 Option)的函数里,expr? 表示「失败就把错误原样返回给调用方,成功就取出值继续」——一行替代一整个 match,是实战代码的标准写法。
示例
Option 的三种打开方式:
fn first_even(numbers: &[i32]) -> Option<i32> {
for &n in numbers {
if n % 2 == 0 {
return Some(n);
}
}
None
}
fn main() {
let found = first_even(&[1, 3, 4, 7]);
assert_eq!(found, Some(4));
// 1. 默认值兜底
let v = first_even(&[1, 3, 5]).unwrap_or(-1);
assert_eq!(v, -1);
// 2. map:有值就变换,结果还是 Option
let doubled = first_even(&[1, 4]).map(|n| n * 2);
assert_eq!(doubled, Some(8));
// 3. match:两个分支都处理
match first_even(&[]) {
Some(n) => println!("第一个偶数是 {n}"),
None => println!("没有偶数"),
}
}
Result 与 ? 运算符:
fn parse_port(text: &str) -> Result<u16, String> {
match text.parse::<u16>() {
Ok(n) if n > 0 => Ok(n),
Ok(_) => Err(String::from("端口必须大于 0")),
Err(_) => Err(format!("「{text}」不是合法端口")),
}
}
// ?:Err 直接向上返回,Ok 取值继续
fn describe_service(port_text: &str) -> Result<String, String> {
let port = parse_port(port_text)?;
Ok(format!("服务运行在 {port} 端口"))
}
fn main() {
assert_eq!(describe_service("8080"), Ok(String::from("服务运行在 8080 端口")));
assert!(describe_service("abc").is_err());
assert!(describe_service("0").is_err());
match describe_service("443") {
Ok(desc) => println!("{desc}"),
Err(e) => println!("启动失败:{e}"),
}
}
常见坑
- unwrap 当常规取值:unwrap 遇到 None/Err 直接 panic。示例和测试里用没问题,业务代码优先 unwrap_or、match 或 ?。
- expect 不写人话:expect("不可能失败") 在真的失败时毫无信息量;写清楚当时假设了什么,例如 expect("配置文件必须存在")。
- 在 main 里用 ?:? 要求所在函数返回 Result 或 Option。main 想直接用 ?,可以写 fn main() -> Result<(), Box>。
- 把 Option 当布尔判断后 unwrap:if opt.is_some() { opt.unwrap() } 是反模式,if let Some(v) = opt 一步到位且不会 panic。
小结
Option 取代 null、Result 建模可恢复失败;unwrap_or / map / match / ? 四件套覆盖日常;unwrap 和 expect 是确定有值时的快行道。下一章看两个最常用的集合:Vec 与 String。