可恢复的错误是那些完全停止程序并不严重的错误。可以处理的错误称为可恢复错误。
它由Result <T,E>
表示。 结果<T,E>
是枚举,由两个变体组成,即OK <T>
和Err <E>
,它描述了可能的错误。
OK <T>
:’T’是一种值,它在成功情况下时返回OK
变量,这是一个预期的结果。Err <E>
:’E’是一种错误,它在失败情况下时返回ERR
变量,这是意想不到的结果。
Enum Result<T,E>
{
OK<T>,
Err<E>,
}
- 在上面的例子中,
Result
是枚举类型,OK <T>&Err <E>
是枚举类型的变体,其中'T'
和'E'
是泛型类型参数。 'T'
是一种值,它将在成功的情况下返回,而'E'
是一种错误,它将在失败的情况下返回。Result
包含泛型类型参数,因此在成功和失败值可能不同的许多不同情况下使用标准库中定义的结果类型和函数。
下面来看一个返回Result
值的简单示例:
use std::fs::File;
fn main()
{
let f:u32 = File::open("vector.txt");
}
执行上面示例代码,得到以下结果 -
在上面的示例中,Rust编译器显示该类型不匹配。 'f'
是u32
类型,而File::open
返回Result <T,E>
类型。 上面的输出显示成功值的类型是std::fs::File
,错误值的类型是std::io::Error
。
注意:
File::open
返回类型是成功值或失败值。 如果File::open
成功,则返回文件句柄,如果File::open
失败,则返回错误值。 结果枚举提供此信息。- 如果
File::open
成功,则f
将具有包含文件句柄的OK
变体,如果File::open
失败,则f将具有包含与错误相关的信息的Err
变体。
匹配表达式以处理结果变体
下面来看看一个匹配表达式的简单示例:
use std::fs::File;
fn main()
{
let f = File::open("vector.txt");
match f
{
Ok(file) => file,
Err(error) => {
panic!("There was a problem opening the file: {:?}", error)
},
};
}
执行上面示例代码,得到以下结果 -
程序说明
- 在上面的示例中,可以直接访问枚举变体,而无需在
OK
和Err
变体之前使用Result::
。 - 如果结果正常,则返回文件并将其存储在
'f'
变量中。 在匹配之后,可以在文件中执行读取或写入操作。 - 匹配对
Err
值起作用。 如果Result
返回Error
值,那么panic!
运行并停止程序的执行。
出错时 Error:unwrap()
- 结果
<T,E>
有许多方法可以提供各种任务。 其中一种方法是unwrap()
方法。unwrap()
方法是匹配表达式的快捷方法。unwrap()
方法和匹配表达式的工作方式是一样的。 - 如果
Result
值是OK
变量,则unwrap()
方法返回OK
变量的值。 - 如果
Result
值是Err
变量,那么unwrap()
方法会调用panic!
宏。
下面来看一个简单的示例:
use std::fs::File;
fn main()
{
File::open("hello.txt").unwrap();
}
执行上面示例代码,得到以下结果:
在上面的例子中,unwrap()
方法会自动调用panic!
宏显示错误信息。
Error: expect()
expect()
方法的行为方式与unwrap()
方法相同,即两种方法都会引起panic!
宏显示错误信息。expect()
和unwrap()
方法之间的区别在于,错误消息作为参数传递给expect()
方法,而unwrap()
方法不包含任何参数。 因此,可以说expect()
方法可以跟踪panic!
的来源更容易。
下面看看一个简单示例 -
use std::fs::File;
fn main()
{
File::open("hello.txt").expect("Not able to find the file hello.txt");
}
执行上面示例代码,得到以下结果 -
在上面的输出中,错误消息显示在程序中指定的输出屏幕上,即“无法找到文件hello.txt”
,这会更容易找到错误代码的位置。 如果包含多个unwrap()
方法,那么很难找到unwrap()
方法引发panic!
显示所有错误消息。
传播错误
传播错误是一种将错误从一个函数转发到另一个函数的机制。 错误传播到调用函数,其中有更多信息可用,以便可以处理错误。 假设有一个名为‘a.txt’ 的文件,它包含文本“yiibai” 。想要创建一个程序来读取文件,先参考下面一个简单的例子:
use std::io;
use std::io::Read;
use std::fs::File;
fn main()
{
let a = read_username_from_file();
print!("{:?}",a);
}
fn read_username_from_file() -> Result<String, io::Error>
{
let f = File::open("a.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}
程序说明 -
read_username_from_file()
函数返回Result <T,E>
类型的值,其中'T'
是String
类型,'E'
是io
类型:Error
。- 如果函数成功,则返回一个包含
String
的OK
值,如果函数失败,则返回Err
值。 - 函数中首先调用
File::open
函数。 如果File::open
函数失败,那么匹配的第二个句柄将返回Err
值,如果File::open
函数成功,则它将文件句柄的值存储在变量f
中。 - 如果
File::open
函数成功,那么创建一个String
的变量。 如果read_to_string()
方法成功,则返回文件的文本,否则返回错误信息。 - 假设我们有一个名为‘a.text’ 的外部文件,并包含文本“yiibai” 。 因此,该程序读取文件‘a.text’ 并显示文件的内容。
传播错误的捷径:?
操作符
使用?
运算符减少了代码的长度。?
运算符是匹配表达式的替换意味着?
运算符的工作方式与匹配表达式的工作方式相同。 假设有一个名为 a.txt 的文件,它包含文本 - “yiibai”
。想要创建一个程序来对该文件执行读取操作。
看看下面一个简单的例子。
use std::io;
use std::io::Read;
use std::fs::File;
fn main()
{
let a = read_username_from_file();
print!("{:?}",a);
}
fn read_username_from_file() -> Result<String, io::Error>
{
let mut f = File::open("a.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
在上面的例子中,?
运算符是在Result
值类型之前使用。 如果Result
为OK
,则返回OK
变体的值,如果Result
为Err
,则返回错误信息。
?运算符和匹配表达式的区别
- 使用
?
运算符的错误通过’from’函数移动,’from’函数在标准库中的from trait
中定义。 - 当
?
运算符调用’from’函数,然后此函数将错误类型转换为当前函数的返回类型中定义的错误类型。 - 如果没有错误发生,那么
?
运算符任何函数末尾的运算符返回OK
的值,如果发生错误,则返回Err
的值。 - 它使函数的实现更简单。
链方法在 ?运算符之后调用
甚至可以通过在?
运算符之后使用链接方法调用来缩短程序的代码。
下面来看一个简单的例子:
use std::io;
use std::io::Read;
use std::fs::File;
fn main()
{
let a = read_username_from_file();
print!("{:?}",a);
}
fn read_username_from_file() -> Result<String, io::Error>
{
let mut s = String::new();
File::open("a.txt")?.read_to_string(&mut s)?;
Ok(s)
}
程序说明
在上面的例子中,将read_to_string()
的调用链接到File::open("a.txt")?
的调用结果。如果两个函数(即read_to_string()
和File::open("a.txt")
成功,则返回OK
值,否则返回错误值。
?运算符的限制
?
运算符只能在返回Result
类型值的函数中使用。 ?
运算符与匹配表达式的工作方式类似。 匹配表达式仅适用于Result
返回类型。
下面通过一个简单的例子来理解这一点。
use std::fs::File;
fn main()
{
let f = File::open("a.txt")?;
}