Rust学习笔记

Rust编程语言入门教程课程笔记

参考教材: The Rust Programming Language (by Steve Klabnik and Carol Nichols, with contributions from the Rust Community)

Lecture 9: Error Handling

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use std::error::Error;
use std::io::ErrorKind;
use std::fs::File;
use std::io::Read;
use std::net::IpAddr;
use std::io;


fn main() -> Result<(), Box<dyn Error>> {//Box<dyn Error> means the function will return a type that implements the Error trait

// Rust groups errors into two major categories: recoverable and unrecoverable errors.
// For a recoverable error, such as a file not found error, it’s reasonable to report
// the problem to the user and retry the operation. Unrecoverable errors are always
// symptoms of bugs, like trying to access a location beyond the end of an array.

// Recoverable errors are instances of the Result<T, E> enum, which has variants
// Ok(T), representing success and containing a value, and Err(E), representing error
// and containing an error value. The Result<T, E> enum is defined as part of the
// standard library.

// The panic! macro can be used to generate a panic and start unwinding its stack.
// While unwinding, the runtime will take care of freeing all the resources owned by
// the thread by calling the destructors of all its objects.

let f = File::open("hello.txt");
match f {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {:?}", error),
};

let _f = File::open("hello.txt").unwrap();//unwrap returns the Ok value inside the Ok variant
//unwrap cannot define the type of the error so it will panic if it is Err

let _f = File::open("hello.txt").expect("Failed to open hello.txt");//expect is similar to unwrap but it allows us to choose the panic message

//matching on different errors
let f = File::open("hello.txt");
let _f = match f {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(error) => panic!("Problem creating the file: {:?}", error),
},
other_error => panic!("Problem opening the file: {:?}", other_error),
},
};

let _f = File::open("hello.txt").unwrap_or_else(|error| {//unwrap_or_else takes a closure
if error.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("Problem creating the file: {:?}", error);
})
} else {
panic!("Problem opening the file: {:?}", error);
}
});

//propagating errors
let _ = read_username_from_file();

//shortcut for propagating errors: the ? operator
let _ = _read_username_from_file();
//the ? operator can only be used in functions that return Result

//translating errors from one type into another using From trait
//when implementing the From trait, the standard library provides a generic
//implementation of From<T> for any type T that implements the Into<U> trait
//this is useful when we want to return a specific error type but the function
//we are calling returns a generic error type

//panic! vs Result

let _home: IpAddr = "127.0.0.1"
.parse()
.expect("Hardcoded IP address should be valid");

//create a custom type for the error
let guess = Guess::new(99);//the constructor will check if the value is valid
println!("Guess value: {}", guess.value());

//use ? in main
let _greeting_file = File::open("hello.txt")?;
Ok(()) //fn main() -> Result<(), Box<dyn Error>>
}

fn read_username_from_file() -> Result<String, io::Error> {
let f = File::open("hello.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),//early return
};

let mut s = String::new();

match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),//early return
}
}

pub struct Guess {
value: i32,
}

impl Guess {
pub fn new(value: i32) -> Guess {
if value < 1 || value > 100 {
panic!("Guess value must be between 1 and 100, got {}.", value);
}

Guess { value }
}

pub fn value(&self) -> i32 {//we need to return a reference to the value
self.value
}
}

fn _read_username_from_file() -> Result<String, io::Error> {
let mut f = File::open("hello.txt")?;//? can only be used in functions that return Result
let mut s = String::new();
f.read_to_string(&mut s)?;//? can only be used in functions that return Result
Ok(s)
} //? can be chained to simplify the code