update repo layout

This commit is contained in:
sunface
2022-03-23 20:04:00 +08:00
parent e2d3027b60
commit c1d8b06518
111 changed files with 958 additions and 4 deletions

View 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)

View 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'
```

View 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(())
}
```