Article directory
foreword
Rust has a unique mechanism for handling exceptions, which is not as simple as the try mechanism in other languages.
Errors in Rust fall into two broad categories: recoverable errors and unrecoverable errors. Most programming languages use Exception
(exception) classes to represent errors. There are no Exceptions in Rust. Use the Result<T, E>
class and panic!
macros for unrecoverable errors.
1. Unrecoverable error
- Caused by a logical error that cannot be resolved in programming, such as accessing a location other than the end of an array .
1.1. Use of panic! macro
The use of macros is relatively simple, let's look at a concrete example:
fn main() {
panic!("Error occured");
println!("Hello, rust");
}
operation result:
Obviously, the program does not run as expected println!("Hello, rust")
, but stops running when the panic!
macro is called. Unrecoverable errors will definitely cause the program to be fatally hit and terminate.
1.2. Analyze the cause of the error through the Powershell command line
Let's analyze the error message in the terminal command line:
thread 'main' panicked at 'Error occured', src\main.rs:2:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
- The first line outputs the location of the panic! macro call and the error message it outputs
- The second line is a prompt, translated into Chinese is " run through the
RUST_BACKTRACE=full
environment variable to display the traceback ". Next look at the traceback (backtrace
) information:
stack backtrace:
0: std::panicking::begin_panic_handler
at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\panicking.rs:584
1: core::panicking::panic_fmt
at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\core\src\panicking.rs:142
2: error_deal::main
at .\src\main.rs:2
3: core::ops::function::FnOnce::call_once<void (*)(),tuple$<> >
at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3\library\core\src\ops\function.rs:248
Backtracking is another way of dealing with unrecoverable errors, it unwinds the running stack and outputs all the information, and the program still exits. Through a lot of output information, we can find the error triggered by the panic! macro we wrote.
2. Recoverable errors
- If accessing a file fails, it may be because it is being occupied, which is normal, and we can solve it by waiting .
2.1, the use of Rustlt<T, E> enumeration classes
This concept is very similar to the exception in the Java programming language. In the C language, we often set the function return value as an integer to express the error encountered by the function. In Rust, the Result<T, E>
enumeration class is used as the return value to express the exception:
enum Result<T, E> {
Ok(T),
Err(E),
}//T的类型不定,相当于C++中模板的写法
We know that it
enum
is often used inmatch
conjunction with, and when a match is foundOK
, the corresponding code will be executed.
The return values of functions in the Rust standard library that can generate exceptions are all of Result
type .
For example: when we try to open a file:
use std::fs::File;
fn main() {
let fp = File::open("hello_rust.txt");
match fp {
Ok(file) => {
println!("File opened successfully.");
},
Err(err) => {
println!("Failed to open the file.");
}
}
}//OK里的参数file是File类型,相当于填充了枚举里的T类型
If the
hello_rust.txt
file does not exist, Failed to open the file will be printed.
Of course, the if let
pattern can simplify the match
syntax block:
use std::fs::File;
fn main() {
let fp = File::open("hello_rust.txt");
if let Ok(file) = fp {
println!("File opened successfully.");
} else {
println!("Failed to open the file.");
}
}
2.2, the unwrap() and expect(message: &str) methods of the Result class
- Treat a recoverable error as an unrecoverable error
for example:
use std::fs::File;
fn main() {
let fp1 = File::open("hello_rust.txt").unwrap();
let fp2 = File::open("hello_rust.txt").expect("Failed to open.");
}
- This program is equivalent to calling the panic! macro
Err
when . - The difference between the two is the
expect
ability to send a specified error message to the panic! macro panic!
macro is an unrecoverable error, this completes the transition
3. Recoverable error delivery
What I talked about before is the processing method of receiving an error. Next, I will talk about how to pass the error information out.
Let's start by writing a function:
fn f(i: i32) -> Result<i32, bool> {
if i >= 0 {
Ok(i)
}
else {
Err(false)
}
}
fn main() {
let r = f(10000);
if let Ok(v) = r {
println!("Ok: f(-1) = {}", v);
} else {
println!("Err");
}
}//运行结果:Ok: f(-1) = 10000
r
The result here isf
returned by the functionok(10000)
, and the value afterif let
pattern matchingv
is 10000
The function in this program f
is the source of the error, now let's write another function that passes the error g
:
fn g(i: i32) -> Result<i32, bool> {
let t = f(i);
return match t {
Ok(i) => Ok(i),
Err(b) => Err(b)
};
}
The function g passes the possible errors of the function f, which is a bit verbose. In Rust, you can add the ?
operator Err
to pass the same type directly:
fn f(i: i32) -> Result<i32, bool> {
if i >= 0 {
Ok(i) }
else {
Err(false) }
}
fn g(i: i32) -> Result<i32, bool> {
let t = f(i)?;
Ok(t) // 因为确定 t 不是 Err, t 在这里已经推导出是 i32 类型
}
fn main() {
let r = g(10000);
if let Ok(v) = r {
println!("Ok: g(10000) = {}", v);
} else {
println!("Err");
}
}//运行结果:Ok: g(10000) = 10000
?
The actual function of the operator is to directly take out the non-abnormal value of the Result class, and return the abnormal Result if there is an exception. Therefore, the ? operator is only used for functions whose return value type is Result<T, E>, and theE
type must be?
the same as the E type of the Result being processed.
4. Handle exceptions with the kind method
Although it was mentioned earlier that exceptions in Rust are not as simple as other languages, that doesn't mean that Rust can't do it: we could implement the try
block in a separate function and pass all the exceptions out for resolution.
This is actually the way a well-differentiated program should be programmed: it should focus on the integrity of individual functions.
But this needs to judge the Err type of Result, the function to get the Err type iskind()
Make an instance of opening a file:
use std::io;
use std::io::Read;
use std::fs::File;
fn read_text_from_file(path: &str) -> Result<String, io::Error> {
let mut f = File::open(path)?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
fn main() {
let str_file = read_text_from_file("hello_rust.txt");
match str_file {
Ok(s) => println!("{}", s),
Err(e) => {
match e.kind() {
io::ErrorKind::NotFound => {
println!("No such file");
},
_ => {
println!("Cannot read the file");
}
}
}
}
}//这里我没有创建hello_rust.txt文件,因此运行结果为:No such file
Code Explanation:
-
Using a
read_text_from_file()
function to pass the result of opening a file to astr_file
variable- It does not exist here
hello_rust.txt
, soFile::open(path)?
the file will not be opened, and the exception will be storedf
in f.read_to_string(&mut s)?
Can't read the file content,ok(s)
no content
- It does not exist here
-
Through analysis, the branch will execute
Err(e)
the code block, usee.kind()
the error type andmatch
branch again- If it is
NotFound
an error, it will print No such file - Other errors are prompted Cannot read the file
- If it is
Rust 的错误处理到此分享结束,欢迎大家指点,如有不恰当的地方还请不吝提出,让我们在交流中进步!