0. Result模块简介
使用Result类型进行错误处理。
Result<T, E>是用于返回和传播错误的类型。它是一个枚举,包含变体Ok(T),表示成功并包含一个值;以及Err(E),表示错误并包含一个错误值。
enum Result<T, E> {
Ok(T),
Err(E),
}
函数在预期出现可恢复错误时返回Result。在std crate中,Result最常用于I/O。
一个返回Result的简单函数可以如下定义和使用:
#[derive(Debug)]
enum Version {
Version1,
Version2,
}
fn parse_version(header: &[u8]) -> Result<Version, &'static str> {
match header.get(0) {
None => Err("invalid header length"),
Some(&1) => Ok(Version::Version1),
Some(&2) => Ok(Version::Version2),
Some(_) => Err("invalid version"),
}
}
fn main() {
let version = parse_version(&[1, 2, 3, 4]);
match version {
Ok(v) => println!("working with version: {v:?}"),
Err(e) => println!("error parsing header: {e:?}"),
}
}

1. Result必须被使用(Rust程序设计语言中提过了)
使用返回值指示错误的一个常见问题是,很容易忽略返回值,从而导致错误未被处理。Result被标注了#[must_use]属性,当Result值被被忽略时,编译器会发出警告。这使得Result特别适合于那些可能遇到错误但不返回有用值的函数。
考虑为I/O类型定义write_all方法,它属于Write trait:
use std::io;
trait Write {
fn write_all(&mut self, bytes: &[u8]) -> Result<(), io::Error>;
}
这个方法不产生值,但写入可能失败。处理错误情况至关重要,不要写成这样:
use std::fs::File;
use std::io::prelude::*;
fn main() {
let mut file = File::create("valuable_data.txt").unwrap();
// 如果write_all出错,我们将永远不知道,因为返回值被忽略了。
file.write_all(b"important message");
}
如果你在Rust中这样写,编译器会给你一个警告。
如果你不想处理错误,可以用expect简单地断言成功。如果写入失败,它会panic,并提供一个大致有用的消息说明原因。
use std::fs::File;
use std::io::prelude::*;
fn main() {
let mut file = File::create("valuable_data.txt").unwrap();
file.write_all(b"important message").expect("failed to write message");
}
用?将错误向上传播到调用栈:
fn write_message() -> io::Result<()> {
let mut file = File::create("valuable_data.txt")?;
file.write_all(b"important message")?;
Ok(())
}
2. 问号运算符?
当编写调用许多返回Result类型函数的代码时,错误处理可能很繁琐。问号运算符?隐藏了一些将错误向上传播到调用栈的样板代码。
它将这段代码:
use std::fs::File;
use std::io::prelude::*;
use std::io;
struct Info {
name: String,
age: i32,
rating: i32,
}
fn write_info(info: &Info) -> io::Result<()> {
// 出错提前返回
let mut file = match File::create("my_best_friends.txt") {
Err(e) => return Err(e),
Ok(f) => f,
};
if let Err(e) = file.write_all(format!("name: {}\n", info.name).as_bytes()) {
return Err(e);
}
if let Err(e) = file.write_all(format!("age: {}\n", info.age).as_bytes()) {
return Err(e);
}
if let Err(e) = file.write_all(format!("rating: {}\n", info.rating).as_bytes()) {
return Err(e);
}
Ok(())
}
替换为:
use std::fs::File;
use std::io::prelude::*;
use std::io;
struct Info {
name: String,
age: i32,
rating: i32,
}
fn write_info(info: &Info) -> io::Result<()> {
// 出错时提前返回
let mut file = File::create("my_best_friends.txt")?;
file.write_all(format!("name: {}\n", info.name).as_bytes())?;
file.write_all(format!("age: {}\n", info.age).as_bytes())?;
file.write_all(format!("rating: {}\n", info.rating).as_bytes())?;
Ok(())
}
在表达式末尾加上?,如果结果是Ok则获取其解包后的值;如果是Err,则提前从外层函数返回该Err。?可用于返回Result的函数,因为它提供了Err的提前返回功能。
3. 内存表示(暂时不知道这部分讲得什么玩意)
Rust保证:当T或E中有一个类型满足Option的内存表示保证(称该类型为I),且另一个类型是大小为零且对齐为1的类型(即1-ZST)时,Result<T, E>与I(因此也与Option<I>)具有相同的大小、对齐和ABI。
如果I是T,那么将类型为I的值t transmute为Result<T, E>(得到Ok(t)),以及将类型为Result<T, E>的值Ok(t) transmute为类型I(得到值t),没有运行时开销。如果I是E,同理,只是将Ok替换为Err。
参考
1、Rust std::result

334

被折叠的 条评论
为什么被折叠?



