4.7 Error Handling
Sometimes an error occurs in the program. It's best to have a plan to deal with the inevitable. Rust has a rich way of handling errors.
There are two types of errors in your program: failures and crashes. Let's discuss the differences and then discuss how to deal with them. Then we discuss the escalation of the error to crash.
Page 107
4.7.1 Failures and crashes
Rust uses two types of errors: failure and crash. Failure is a kind of error that can be redeemed. It's not going to crash. " What do you mean by "redemption"? In most cases, errors are likely to occur. For example, a parse function:
"5". Parse ();
This method converts one character to another type. But since he is a character, you cannot guarantee that the conversion will work. For example, what type should it be converted to?
"Hello5world". Parse ();
This doesn't work. So we know that this function can only work with some input. This is an expected behavior. We call this error a failure (failure).
On the other hand, sometimes we encounter unexpected errors or mistakes that we can't undo. A classic example of assert!:
Assert! (x = = 5);
We use assert! to declare that an expression is true. If not, then there will be an error. This error causes us to continue with the current state. Another example is the use of unreachable! () macro:
Enum Event {
Newrelease,
}
fn Probability (_: &event), f64 {
Real implementation would is more complex, of course
0.95
}
fn descriptive_probability (event:event) & ' static str {
Match probability (&event) {
1.00 = "certain",
0.00 = "Impossible",
0.00 ... 0.25 = "very unlikely",
0.25 ... 0.50 = "Unlikely",
0.50 ... 0.75 = "Likely",
0.75 ... 1.00 = "very likely",
}
}
fn Main () {
Std::io::p rintln! (Descriptive_probability (newrelease));
}
We got an error:
Although we know we covered all the possibilities, rust can't. He didn't know. The range is from 0.0 to 1.0. So we need to add a case:use event::newrelease;
Enum Event {
Newrelease,
}
fn Probability (_: &event), f64 {
Real implementation would is more complex, of course
0.95
}
fn descriptive_probability (event:event) & ' static str {
Match probability (&event) {
1.00 = "certain",
0.00 = "Impossible",
0.00 ... 0.25 = "very unlikely",
0.25 ... 0.50 = "Unlikely",
0.50 ... 0.75 = "Likely",
0.75 ... 1.00 = "Very likely", _ = = unreachable! ()
}
}
fn Main () {
println! ("{}", descriptive_probability (Newrelease));
}
We don't have to add this _case all the time, so we use unreachable! () macro pointed out this point. It is another way of handling error. Rust calls this error a panic.
4.7.2 using option and result to handle errors
One of the simplest ways to handle a function failure is to use the option<t> type. For example, the Find method tries to find a matching string in the strings and then returns an option:
Let S = "foo";
Assert! (S.find (' F '), Some (0)); assert! (S.find (' z '), None);
This is basically the simplest way, but in that case we don't know the error message. What if we want to know why we failed? To do this, we need to use the result<t,e> type. Like this:
Enum Result<t, e> {Ok (T), ERR (E)}
This enumeration has the rust language itself provided, so you don't have to define it. The Ok (T) variable represents success, and the ERR (E) variable represents a failure. Returning a result type instead of an option type is the recommended method unless you do not care about the failure information.
There is one:
#[derive (Debug)]
Enum Version {Version1, Version2}
#[derive (Debug)]
Enum ParseError {invalidheaderlength, invalidversion}
fn Parse_version (header: &[u8]), result<version, parseerror> {
If Header.len () < 1 {
Return ERR (parseerror::invalidheaderlength);
}
Match Header[0] {
1 = Ok (Version::version1),
2 = Ok (Version::version2),
_ ~ = ERR (parseerror::invalidversion)
}
}
Let Version = Parse_version (&[1, 2, 3, 4]);
Match Version {
Ok (v) + = {
println! ("Working with version: {:?}", V);
}
ERR (e) + = {
println! ("Error parsing header: {:?}", E);
}
}
This function uses an enumeration, parseerror, to enumerate the different errors. The debug feature can be that we pass {:?} Format operator to print an enumeration value.
4.7.3 panic! non-recoverable error
When an unexpected or unrecoverable error occurs, the panic! macro triggers a crash. It causes the current thread to crash and then gives an error:
panic! ("Boom");
Output
Thread ' <main> ' panicked at ' boom ', hello.rs:2
Because this happens rarely, use it sparingly.
4.7.4 failed to escalate to crash
In some cases, even when a function fails, we want to treat it as if it had been given a crash rather than a failure. For example, Io::stdin (). Read_line (&mut buffer) returned a result<usize> Occurs when a row of input fails to read. This is where we can handle this failure and recover it.
If we do not want to deal with this error, we would like to terminate the program run, we can try the unwrap () method.
Io::stdin (). Read_line (&mut buffer). Unwrap ();
When result is an err, unwrap () will panic!. This means "give me the value, if there's something wrong, let the program crash." This is a bit less reliable than trying to recover a program, but it's more concise. Sometimes, the crash is right.
Another approach is better than unwrap ():
Let Mut buffer = string::new (), let input = Io::stdin (). Read_line (&mut buffer). OK () . Expect ("Failed to read line");
Ok () converts result to option,expect () does the same thing to unwrap (), but contains a message. This message is passed to the potential panic!, which provides a better error with the error message.
Page 111
4.7.5 using try!
Error handling can be tedious to write when multiple functions are called to return the result type. Use the try! macro to hide the error messages that are pervasive on the call stack. Put the following code: using Std::fs::file;
Use Std::io;
Use Std::io::p relude::*;
struct Info {
Name:string,
AGE:I32,
RATING:I32,
}
fn Write_info (info: &info), io::result< () > {
Let Mut file = File::create ("My_best_friends.txt"). Unwrap ();
If let ERR (e) = writeln! (&mut file, "name: {}", Info.name) {
Return ERR (E)
}
If let ERR (e) = writeln! (&mut file, "Age: {}", info.age) {
Return ERR (E)
}
If let ERR (e) = writeln! (&mut file, "rating: {}", info.rating) {
Return ERR (E)
}
Return Ok (());
Replaced by: Use Std::fs::file;
Use Std::io;
Use Std::io::p relude::*;
struct Info {
Name:string,
AGE:I32,
RATING:I32,
}
fn Write_info (info: &info), io::result< () > {let mut file = try! ( File::create ("My_best_friends.txt"));
try! (writeln! (&mut file, "name: {}", info.name));
try! (writeln! (&mut file, "Age: {}", info.age));
try! (writeln! (&mut file, "rating: {}", info.rating));
Return Ok (());
}
Wrapping the code in try! will result in a success value, unless result is err, the function will return in advance.
Note that you can only use try! when a function returns result, which means you cannot use try! in the main () function, because the main () function does not return any values.
Try! used from<error> to decide what error to return.
Rust Chinese Translation 21