sui_rust/ch02/s7_error_handle.rs
1//! 第二章:Rust核心概念
2//! 2.6 错误处理
3//!
4//! 1. 类型系统保证函数契约
5//! 2. 断言用于防御
6//! 3. Option<T> 消除空指针失败
7//! 4. Result<T, E> 传播错误
8//! 5. Panic 恐慌崩溃
9//!
10
11/**
12
13
14# 消除失败
15
161. 类型系统保证函数契约
17
18```rust
19fn sum(a: i32, b: i32) -> i32 {
20 a + b
21}
22fn main() {
23 sum(1u32, 2u32); // 违反契约,报错
24}
25```
26
272. 断言用于防御
28
29```rust
30
31fn extend_vec(v: &mut Vec<i32>, i: i32) {
32 assert!(v.len() == 5);
33 v.push(i);
34}
35fn main() {
36 let mut vec = vec![1, 2, 3];
37 extend_vec(&mut vec, 4);
38 extend_vec(&mut vec, 5);
39 extend_vec(&mut vec, 6); // panic!
40}
41```
42*/
43pub fn elim_failure() {
44 println!("Eliminate Failure!");
45}
46
47/**
48
49# 分层错误处理
50
51错误处理:顾名思义,处理错误。既然要处理错误,那肯定是指开发者可以处理的情况。
52
53- Option: 「有」与「无」
54- Result:「对」与「错」
55
56### Option
57
58```rust
59fn get_shortest(names: Vec<&str>) -> Option<&str> {
60 if names.len() > 0 {
61 let mut shortest = names[0];
62 for name in names.iter() {
63 if name.len() < shortest.len() {
64 shortest = *name;
65 }
66 }
67 Some(shortest)
68 } else {
69 None
70 }
71}
72fn show_shortest(names: Vec<&str>) -> &str {
73 match get_shortest(names) {
74 Some(shortest) => shortest,
75 None => "Not Found",
76 }
77}
78fn main(){
79 assert_eq!(show_shortest(vec!["Uku", "Felipe"]), "Uku");
80 assert_eq!(show_shortest(Vec::new()), "Not Found");
81}
82```
83
84使用 Match 在 「盒内」处理 Option
85
86```rust
87fn get_shortest_length(names: Vec<&str>) -> Option<usize> {
88 match get_shortest(names) {
89 Some(shortest) => Some(shortest.len()),
90 None => None,
91 }
92}
93fn main(){
94 assert_eq!(get_shortest_length(vec!["Uku","Felipe"]),Some(3));
95 assert_eq!(get_shortest_length(Vec::new()), None);
96}
97```
98
99使用标准库内建组合子处理:
100
101```rust
102fn double(value: f64) -> f64 {
103 value * 2.
104}
105fn square(value: f64) -> f64 {
106 value.powi(2 as i32)
107}
108fn inverse(value: f64) -> f64 {
109 value * -1.
110}
111fn log(value: f64) -> Option<f64> {
112 match value.log2() {
113 x if x.is_normal() => Some(x),
114 _ => None
115 }
116}
117fn sqrt(value: f64) -> Option<f64> {
118 match value.sqrt() {
119 x if x.is_normal() => Some(x),
120 _ => None
121 }
122}
123fn main () {
124 let number: f64 = 20.;
125 let result = Option::from(number)
126 .map(inverse).map(double).map(inverse)
127 .and_then(log).map(square).and_then(sqrt);
128 match result {
129 Some(x) => println!("Result was {}.", x),
130 None => println!("This failed.")
131 }
132}
133```
134
135
136### Result
137
138- Error trait:[https://doc.rust-lang.org/stable/std/error/trait.Error.html](https://doc.rust-lang.org/stable/std/error/trait.Error.html)
139- Result Error Handle : [read-sum crate]
140- `?` and [std::ops::Try](https://doc.rust-lang.org/stable/std/ops/trait.Try.html)
141
142
143
144
145```rust
146use std::num::ParseIntError;
147// fn square(number_str: &str) -> Result<i32, ParseIntError>
148// {
149// number_str.parse::<i32>().map(|n| n.pow(2))
150// }
151type ParseResult<T> = Result<T, ParseIntError>;
152fn square(number_str: &str) -> ParseResult<i32>
153{
154 number_str.parse::<i32>().map(|n| n.pow(2))
155}
156fn main() {
157 match square("10") {
158 Ok(n) => assert_eq!(n, 100),
159 Err(err) => println!("Error: {:?}", err),
160 }
161}
162```
163
164
165*/
166pub fn error_handle() {
167 println!("Error Handle!")
168}
169
170/**
171
172### panic 的两种类型:
173
174- Unwinding(栈展开)。
175- Aborting(中止)。
176
177Unwinding 可以使应用程序线程以相对干净的方式关闭。
178回收所有分配的系统资源,正确删除所有应用程序对象,依此类推。
179此外,恐慌停止在有问题的线程的边界,而不是杀死整个应用程序过程。
180所有这一切意味着,如果所有对象都具有明智的析构函数,则尽管有困难,但仍可以从紧急情况中恢复应用程序。
181
182如果你应用程序是为此目的而设计的,则可以检测到线程紧急情况并重新启动有问题的线程,希望该操作能够正确恢复。
183在无法关闭应用程序的情况下,例如在关键系统中,类似于Erlang的容错方法可能是有意义的。
184
185对于Aborting,不存在应用程序恢复的可能性。一旦某些代码中止,应用程序进程将立即终止,这意味着要实现容错功能,就需要进行更加复杂的多进程设计。
186另外,由于未运行资源析构函数,因此整个系统可能处于不一致状态,这意味着重新启动应用程序可能非常不容易。
187
188总而言之,仅应在确实不关心应用程序立即崩溃并可能破坏在崩溃过程中操作的任何硬件/操作系统状态的情况下启用Aborting恐慌。
189
190需要了解一个事实,Rust 目前对 OOM(out of memory)对处理是直接 Aborting ,无论你如何设置Panic类型。
191
192### 恐慌安全:
193
194[Rust Magazine #01 security](https://rustmagazine.github.io/rust_magazine_2021/chapter_1/rust_security_part1.html)
195- catch_unwind
196
197
198```rust
199
200use std::panic;
201fn sum(a: i32, b: i32) -> i32{
202 a + b
203}
204fn main() {
205 let result = panic::catch_unwind(|| { println!("hello!"); });
206 assert!(result.is_ok());
207 let result = panic::catch_unwind(|| { panic!("oh no!"); });
208 assert!(result.is_err());
209 println!("{}", sum(1, 2));
210}
211```
212
213使用 set_hook
214
215```rust
216use std::panic;
217fn sum(a: i32, b: i32) -> i32{
218 a + b
219}
220fn main() {
221 let result = panic::catch_unwind(|| { println!("hello!"); });
222 assert!(result.is_ok());
223 panic::set_hook(Box::new(|panic_info| {
224 if let Some(location) = panic_info.location() {
225 println!("panic occurred '{}' at {}",
226 location.file(), location.line()
227 );
228 } else {
229 println!("can't get location information...");
230 }
231 }));
232 let result = panic::catch_unwind(|| { panic!("oh no!"); });
233 assert!(result.is_err());
234 println!("{}", sum(1, 2));
235}
236```
237
238
239*/
240pub fn panic_cant_handle() {
241 println!("Panic Can't Handle")
242}