result and ?
Result<T> 是一个枚举类型用于描述返回的结果或错误,它包含两个成员(变体 variants) :
Ok(T): 返回一个结果值 TErr(e): 返回一个错误,e是具体的错误值
简而言之,如果期待一个正确的结果,就返回 Ok,反之则是 Err。
- 🌟🌟
2. // 填空并修复错误
3. use std::num::ParseIntError;
5. fn multiply(n1_str: &str, n2_str: &str) -> __ {
6. let n1 = n1_str.parse::<i32>();
7. let n2 = n2_str.parse::<i32>();
8. Ok(n1.unwrap() * n2.unwrap())
9. }
11. fn main() {
12. let result = multiply("10", "2");
13. assert_eq!(result, __);
15. let result = multiply("t", "2");
16. assert_eq!(result.__, 8);
18. println!("Success!")
19. }
?
? 跟 unwrap 非常像,但是 ? 会返回一个错误,而不是直接 panic.
- 🌟🌟
2. use std::num::ParseIntError;
4. // 使用 `?` 来实现 multiply
5. // 不要使用 unwrap !
6. fn multiply(n1_str: &str, n2_str: &str) -> __ {
7. }
9. fn main() {
10. assert_eq!(multiply("3", "4").unwrap(), 12);
11. println!("Success!")
12. }
- 🌟🌟
2. use std::fs::File;
3. use std::io::{self, Read};
5. fn read_file1() -> Result<String, io::Error> {
6. let f = File::open("hello.txt");
7. let mut f = match f {
8. Ok(file) => file,
9. Err(e) => return Err(e),
10. };
12. let mut s = String::new();
13. match f.read_to_string(&mut s) {
14. Ok(_) => Ok(s),
15. Err(e) => Err(e),
16. }
17. }
19. // 填空
20. // 不要修改其它代码
21. fn read_file2() -> Result<String, io::Error> {
22. let mut s = String::new();
24. __;
26. Ok(s)
27. }
29. fn main() {
30. assert_eq!(read_file1().unwrap_err().to_string(), read_file2().unwrap_err().to_string());
31. println!("Success!")
32. }
map & and_then
map and and_then 是两个常用的组合器( combinator ),可以用于 Result<T, E> (也可用于 Option<T>).
- 🌟🌟
1. use std::num::ParseIntError;
3. // 使用两种方式填空: map, and then
4. fn add_two(n_str: &str) -> Result<i32, ParseIntError> {
5. n_str.parse::<i32>().__
6. }
8. fn main() {
9. assert_eq!(add_two("4").unwrap(), 6);
11. println!("Success!")
12. }
- 🌟🌟🌟
1. use std::num::ParseIntError;
3. // 使用 Result 重写后,我们使用模式匹配的方式来处理,而无需使用 `unwrap`
4. // 但是这种写法实在过于啰嗦..
5. fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
6. match n1_str.parse::<i32>() {
7. Ok(n1) => {
8. match n2_str.parse::<i32>() {
9. Ok(n2) => {
10. Ok(n1 * n2)
11. },
12. Err(e) => Err(e),
13. }
14. },
15. Err(e) => Err(e),
16. }
17. }
19. // 重写上面的 `multiply` ,让它尽量简洁
20. // 提示:使用 `and_then` 和 `map`
21. fn multiply1(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
22. // 实现...
23. }
25. fn print(result: Result<i32, ParseIntError>) {
26. match result {
27. Ok(n) => println!("n is {}", n),
28. Err(e) => println!("Error: {}", e),
29. }
30. }
32. fn main() {
33. let twenty = multiply1("10", "2");
34. print(twenty);
36. // 下面的调用会提供更有帮助的错误信息
37. let tt = multiply("t", "2");
38. print(tt);
40. println!("Success!")
41. }
类型别名
如果我们要在代码中到处使用 std::result::Result<T, ParseIntError> ,那毫无疑问,代码将变得特别冗长和啰嗦,对于这种情况,可以使用类型别名来解决。
例如在标准库中,就在大量使用这种方式来简化代码: io::Result.
- 🌟
1. use std::num::ParseIntError;
3. // 填空
4. type __;
6. // 使用上面的别名来引用原来的 `Result` 类型
7. fn multiply(first_number_str: &str, second_number_str: &str) -> Res<i32> {
8. first_number_str.parse::<i32>().and_then(|first_number| {
9. second_number_str.parse::<i32>().map(|second_number| first_number * second_number)
10. })
11. }
13. // 同样, 这里也使用了类型别名来简化代码
14. fn print(result: Res<i32>) {
15. match result {
16. Ok(n) => println!("n is {}", n),
17. Err(e) => println!("Error: {}", e),
18. }
19. }
21. fn main() {
22. print(multiply("10", "2"));
23. print(multiply("t", "2"));
25. println!("Success!")
26. }
在 fn main 中使用 Result
一个典型的 main 函数长这样:
1. fn main() {
2. println!("Hello World!");
3. }
事实上 main 函数还可以返回一个 Result 类型:如果 main 函数内部发生了错误,那该错误会被返回并且打印出一条错误的 debug 信息。
2. use std::num::ParseIntError;
4. fn main() -> Result<(), ParseIntError> {
5. let number_str = "10";
6. let number = match number_str.parse::<i32>() {
7. Ok(number) => number,
8. Err(e) => return Err(e),
9. };
10. println!("{}", number);
11. Ok(())
12. }
你可以在这里找到答案(在 solutions 路径下)
