mirror of
https://github.com/sunface/rust-by-practice.git
synced 2025-06-25 13:39:41 +00:00
update repo layout
This commit is contained in:
5
en/src/result-panic/intro.md
Normal file
5
en/src/result-panic/intro.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Result and panic
|
||||
Learning resources:
|
||||
- English: [Rust Book 9.1, 9.2](https://doc.rust-lang.org/book/ch09-00-error-handling.html)
|
||||
- 简体中文: [Rust语言圣经 - 返回值和错误处理](https://course.rs/basic/result-error/intro.html)
|
||||
|
111
en/src/result-panic/panic.md
Normal file
111
en/src/result-panic/panic.md
Normal file
@ -0,0 +1,111 @@
|
||||
# panic!
|
||||
The simplest error handling mechanism is to use `panic`. It just prints an error message and starts unwinding the stack, finally exit the current thread:
|
||||
|
||||
- if panic occured in `main` thread, then the program will be exited.
|
||||
- if in spawned thread, then this thread will be terminated, but the program won't
|
||||
|
||||
|
||||
1. 🌟🌟
|
||||
```rust,editable
|
||||
|
||||
// FILL the blanks
|
||||
fn drink(beverage: &str) {
|
||||
if beverage == "lemonade" {
|
||||
println!("Success!");
|
||||
// IMPLEMENT the below code
|
||||
__
|
||||
}
|
||||
|
||||
println!("Excercise Failed if printing out this line!");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
drink(__);
|
||||
|
||||
println!("Excercise Failed if printing out this line!");
|
||||
}
|
||||
```
|
||||
|
||||
## common panic cases
|
||||
2. 🌟🌟
|
||||
```rust,editable
|
||||
// MAKE the code work by fixing all panics
|
||||
fn main() {
|
||||
assert_eq!("abc".as_bytes(), [96, 97, 98]);
|
||||
|
||||
let v = vec![1, 2, 3];
|
||||
let ele = v[3];
|
||||
// unwrap may panic when get return a None
|
||||
let ele = v.get(3).unwrap();
|
||||
|
||||
// Sometimes, the compiler is unable to find the overflow errors for you in compile time ,so a panic will occur
|
||||
let v = production_rate_per_hour(2);
|
||||
|
||||
// because of the same reason as above, we have to wrap it in a function to make the panic occur
|
||||
divide(15, 0);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
|
||||
fn divide(x:u8, y:u8) {
|
||||
println!("{}", x / y)
|
||||
}
|
||||
|
||||
fn production_rate_per_hour(speed: u8) -> f64 {
|
||||
let cph: u8 = 221;
|
||||
match speed {
|
||||
1..=4 => (speed * cph) as f64,
|
||||
5..=8 => (speed * cph) as f64 * 0.9,
|
||||
9..=10 => (speed * cph) as f64 * 0.77,
|
||||
_ => 0 as f64,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn working_items_per_minute(speed: u8) -> u32 {
|
||||
(production_rate_per_hour(speed) / 60 as f64) as u32
|
||||
}
|
||||
```
|
||||
|
||||
### Detailed call stack
|
||||
By default the stack unwinding will only give something like this:
|
||||
```shell
|
||||
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', src/main.rs:4:5
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
```
|
||||
|
||||
Though there is the panic reason and code line showing here, but sometime we want to get more info about the call stack.
|
||||
|
||||
3. 🌟
|
||||
```shell
|
||||
## FILL in the blank to display the whole call stack
|
||||
## Tips: you can find the clue in the default panic info
|
||||
$ __ cargo run
|
||||
thread 'main' panicked at 'assertion failed: `(left == right)`
|
||||
left: `[97, 98, 99]`,
|
||||
right: `[96, 97, 98]`', src/main.rs:3:5
|
||||
stack backtrace:
|
||||
0: rust_begin_unwind
|
||||
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panicking.rs:498:5
|
||||
1: core::panicking::panic_fmt
|
||||
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/panicking.rs:116:14
|
||||
2: core::panicking::assert_failed_inner
|
||||
3: core::panicking::assert_failed
|
||||
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/panicking.rs:154:5
|
||||
4: study_cargo::main
|
||||
at ./src/main.rs:3:5
|
||||
5: core::ops::function::FnOnce::call_once
|
||||
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/ops/function.rs:227:5
|
||||
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
|
||||
```
|
||||
|
||||
### `unwinding` and `abort`
|
||||
By default, when a `panic` occurs, the program starts *unwinding*, which means Rust walks back up the stack and cleans up the data from each function it encouters.
|
||||
|
||||
But this walk back and clean up is a lot of work. The alternative is to immediately abort the program without cleaning up.
|
||||
|
||||
If in your project you need to make the resulting binary as small as possible, you can switch from unwinding to aborting by adding below content to `Cargo.toml`:
|
||||
```toml
|
||||
[profile.release]
|
||||
panic = 'abort'
|
||||
```
|
||||
|
212
en/src/result-panic/result.md
Normal file
212
en/src/result-panic/result.md
Normal file
@ -0,0 +1,212 @@
|
||||
# result and ?
|
||||
`Result<T>` is an enum to describe possible errors. It has two variants:
|
||||
|
||||
- `Ok(T)`: a value T was found
|
||||
- `Err(e)`: An error was found with a value `e`
|
||||
|
||||
In short words, the expected outcome is `Ok`, while the unexpected outcome is `Err`.
|
||||
|
||||
1. 🌟🌟
|
||||
```rust,editable
|
||||
|
||||
// FILL in the blanks and FIX the errors
|
||||
use std::num::ParseIntError;
|
||||
|
||||
fn multiply(n1_str: &str, n2_str: &str) -> __ {
|
||||
let n1 = n1_str.parse::<i32>();
|
||||
let n2 = n2_str.parse::<i32>();
|
||||
Ok(n1.unwrap() * n2.unwrap())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let result = multiply("10", "2");
|
||||
assert_eq!(result, __);
|
||||
|
||||
let result = multiply("t", "2");
|
||||
assert_eq!(result.__, 8);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
### ?
|
||||
`?` is almost exactly equivalent to `unwrap`, but `?` returns instead of panic on `Err`.
|
||||
|
||||
2. 🌟🌟
|
||||
```rust,editable
|
||||
|
||||
use std::num::ParseIntError;
|
||||
|
||||
// IMPLEMENT multiply with ?
|
||||
// DON'T use unwrap here
|
||||
fn multiply(n1_str: &str, n2_str: &str) -> __ {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(multiply("3", "4").unwrap(), 12);
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
3. 🌟🌟
|
||||
```rust,editable
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read};
|
||||
|
||||
fn read_file1() -> Result<String, io::Error> {
|
||||
let f = File::open("hello.txt");
|
||||
let mut f = match f {
|
||||
Ok(file) => file,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
let mut s = String::new();
|
||||
match f.read_to_string(&mut s) {
|
||||
Ok(_) => Ok(s),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
// FILL in the blanks with one code line
|
||||
// DON'T change any code else
|
||||
fn read_file2() -> Result<String, io::Error> {
|
||||
let mut s = String::new();
|
||||
|
||||
__;
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(read_file1().unwrap_err().to_string(), read_file2().unwrap_err().to_string());
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
### map & and_then
|
||||
[map](https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.map) and [and_then](https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.and_then) are two common combinators for `Result<T, E>` (also for `Option<T>`).
|
||||
|
||||
4. 🌟🌟
|
||||
|
||||
```rust,editable
|
||||
use std::num::ParseIntError;
|
||||
|
||||
// FILL in the blank in two ways: map, and then
|
||||
fn add_two(n_str: &str) -> Result<i32, ParseIntError> {
|
||||
n_str.parse::<i32>().__
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(add_two("4").unwrap(), 6);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
5. 🌟🌟🌟
|
||||
```rust,editable
|
||||
use std::num::ParseIntError;
|
||||
|
||||
// With the return type rewritten, we use pattern matching without `unwrap()`.
|
||||
// But it's so Verbose..
|
||||
fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
|
||||
match n1_str.parse::<i32>() {
|
||||
Ok(n1) => {
|
||||
match n2_str.parse::<i32>() {
|
||||
Ok(n2) => {
|
||||
Ok(n1 * n2)
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
// Rewriting `multiply` to make it succinct
|
||||
// You should use BOTH of `and_then` and `map` here.
|
||||
fn multiply1(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
|
||||
// IMPLEMENT...
|
||||
}
|
||||
|
||||
fn print(result: Result<i32, ParseIntError>) {
|
||||
match result {
|
||||
Ok(n) => println!("n is {}", n),
|
||||
Err(e) => println!("Error: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// This still presents a reasonable answer.
|
||||
let twenty = multiply1("10", "2");
|
||||
print(twenty);
|
||||
|
||||
// The following now provides a much more helpful error message.
|
||||
let tt = multiply("t", "2");
|
||||
print(tt);
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
### Type alias
|
||||
Using `std::result::Result<T, ParseIntError>` everywhere is verbose and tedious, we can use alias for this purpose.
|
||||
|
||||
At a module level, creating aliases can be particularly helpful. Errors found in the a specific module often has the same `Err` type, so a single alias can succinctly defined all associated `Results`. This is so useful even the `std` library even supplies one: [`io::Result`](https://doc.rust-lang.org/std/io/type.Result.html).
|
||||
|
||||
6. 🌟
|
||||
```rust,editable
|
||||
use std::num::ParseIntError;
|
||||
|
||||
// FILL in the blank
|
||||
type __;
|
||||
|
||||
// Use the above alias to refer to our specific `Result` type.
|
||||
fn multiply(first_number_str: &str, second_number_str: &str) -> Res<i32> {
|
||||
first_number_str.parse::<i32>().and_then(|first_number| {
|
||||
second_number_str.parse::<i32>().map(|second_number| first_number * second_number)
|
||||
})
|
||||
}
|
||||
|
||||
// Here, the alias again allows us to save some space.
|
||||
fn print(result: Res<i32>) {
|
||||
match result {
|
||||
Ok(n) => println!("n is {}", n),
|
||||
Err(e) => println!("Error: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
print(multiply("10", "2"));
|
||||
print(multiply("t", "2"));
|
||||
|
||||
println!("Success!")
|
||||
}
|
||||
```
|
||||
|
||||
### Using Result in `fn main`
|
||||
Typically `the` main function will look like this:
|
||||
```rust
|
||||
fn main() {
|
||||
println!("Hello World!");
|
||||
}
|
||||
```
|
||||
|
||||
However `main` is also able to have a return type of `Result`. If an error occurs within the `main` function it will return an error code and print a debug representation of the error( Debug trait ).
|
||||
|
||||
The following example shows such a scenario:
|
||||
```rust,editable
|
||||
|
||||
use std::num::ParseIntError;
|
||||
|
||||
fn main() -> Result<(), ParseIntError> {
|
||||
let number_str = "10";
|
||||
let number = match number_str.parse::<i32>() {
|
||||
Ok(number) => number,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
println!("{}", number);
|
||||
Ok(())
|
||||
}
|
||||
```
|
Reference in New Issue
Block a user