Rust has a unique mechanism for handling exception cases, unlike those in other languages
try
the mechanism is so simple.
First of all, there are generally two kinds of errors in the program: recoverable errors and unrecoverable errors.
A typical case of recoverable error is a file access error. If access to a file fails, it may be because it is being occupied and is normal, which we can solve by waiting.
But there is also an error that is caused by logic errors that cannot be resolved in programming, such as accessing locations other than the end of the array.
Most programming languages do not distinguish between these two errors and use the
Exception
(exception) class to represent errors. Not in Rust
Exception
.
For recoverable errors This chapter has not been devoted to the syntax of Rust macros before, but has already been used Running result: Obviously, the program doesn’t run to println! (“Hello, Rust”) as expected, but on panic! The macro stopped running when it was called. An unrecoverable error must cause the program to be fatally hit and stop running. Let’s look at the two lines of error output: The first line outputs The second line is a prompt, which translates into Chinese as “run through the ` RUST_BACKTRACE=1‘environment variable to display backtracking”. Next we will introduce backtrace. Following the example just now, let’s create a new terminal in VSCode: Set environment variables in the newly created terminal (different terminal methods are different, here are two main methods): If you are in a Windows system version of Windows 7 or above, the terminal command line used by default is If you are using a UNIX system such as Linux or macOS, the default is Then you will see the following text: Backtracking is another way to handle unrecoverable errors. It expands the running stack and outputs all the information, and then the program still exits. The ellipsis above omits a lot of output information, and we can findthe This concept is very similar to exceptions in the Java programming language.In fact, in C language, we often set the return value of the function to aninteger to express the error encountered by the function. In Rust, we use the The return values of functions that may produce exceptions in the Rust standard library are of type Result. For example, when we try to open a file: If Of course, we talked about it in the enumeration section. If you want a recoverable error to be handled as an unrecoverable error, the Result class provides two methods: `` unwrap () `` and expect (message: & str): This program is equivalent to Result for We were talking about how to handle receiving errors, but what if we write afunction ourselves and want to pass it on when we encounter an error? Running result: The function f in this program is the source of the error, so now let’s write another function g that passes the error: The function g passes a possible error in the function f (g here is just a simple example, but in fact the function that passes the error usually contains a lot of other operations). It’s a bit lengthy to write like this, and you can use it in Rust Running result: The actual function of the symbol So far, Rust doesn’t seem to be like But this needs to judge Result’s Running result:
Result<T,
E>
class, and for unrecoverable errors``panic!`` macros to deal with. 7.16.1. Unrecoverable error #
println!
Macros, because these macros are relatively simple to use, so we don’t need to master them thoroughly for thetime being. We can use the same method and first learn to use
pan
how to use macros.Example #
fn main() {
panic!("error occured");
println!("Hello, Rust");
}
thread 'main' panicked at 'error occured', src\main.rs:3:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
panic!
the location of the macro call and the error message it outputs.
Powershell
, use the following command:$env:RUST_BACKTRACE=1 ; cargo run
bash
command line, use the following command:RUST_BACKTRACE=1 cargo run
thread 'main' panicked at 'error occured', src\main.rs:3:5
stack backtrace:
...
11: greeting::main
at .\src\main.rs:3
...
panic!
error triggered by macro. 7.16.2. Recoverable error #
Result<T,
E>
, the enumeration class is used as a return value to express the exception:enum Result<T, E> {
Ok(T),
Err(E),
}
Example #
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
match f {
Ok(file) => {
println!("File opened successfully.");
},
Err(err) => {
println!("Failed to open the file.");
}
}
}
hello.txt
file does not exist and will be printed
"Failed
toopen
the
file."
.
if
let
syntaxcan be simplified
match
grammatical block:Example #
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
if let Ok(file) = f {
println!("File opened successfully.");
} else {
println!("Failed to open the file.");
}
}
Example #
use std::fs::File;
fn main() {
let f1 = File::open("hello.txt").unwrap();
let f2 = File::open("hello.txt").expect("Failed to open.");
}
Err
called when the
panic!
Macro. The difference between the two is
expect
, you can talk to panic! The macro sends a specified error message. 7.16.3. Recoverable error delivery #
Example #
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
Example #
fn g(i: i32) -> Result<i32, bool> {
let t = f(i);
return match t {
Ok(i) => Ok(i),
Err(b) => Err(b)
};
}
Result
add after object
?
operator sets the same kind of
Err
pass it directly out:Example #
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) // Because it is certain that t is not Err, t is already of type i32 here
}
fn main() {
let r = g(10000);
if let Ok(v) = r {
println!("Ok: g(10000) = {}", v);
} else {
println!("Err");
}
}
Ok: g(10000) = 10000
?
is to set the
Result
.The non-abnormal value of the class is taken out directly, and if there is an exception, the exception will be taken out.
Result
go back out. Therefore, the
?
symbol is only used to return a value of type
Result<T,
E>
where the function of
E
type must be the same as
?
what is handled
Result
of
E
type is the same. 7.16.4.
kind
Method #
try
block can also make the same kind of exception anywhere directly get the same syntax, but this does not mean that Rust can not be implemented: we can completely put
try
Blocksare implemented in separate functions, passing all exceptions out for resolution. In fact, this is the programming method that a well-differentiated program should follow: attention should be paid to the integrity of independent functions.
Err
type, getting. The function of
Err
type is
kind()
.Example #
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.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");
}
}
}
}
}
No such file