Merge branch 'sunface:master' into lifetimes-practice
This commit is contained in:
commit
358abc0ff6
|
@ -1 +1,2 @@
|
||||||
book
|
book
|
||||||
|
.DS_Store
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
# ChangeLog
|
# ChangeLog
|
||||||
|
|
||||||
|
### 2022-03-11
|
||||||
|
|
||||||
|
- Add one exercise in [Patterns](https://practice.rs/pattern-match/patterns.html)
|
||||||
|
- Add [Result and ?](https://practice.rs/result-panic/result.html)
|
||||||
|
|
||||||
### 2022-03-10
|
### 2022-03-10
|
||||||
|
|
||||||
- Add [Type conversions - From/Into](https://practice.rs/type-conversions/from-into.html)
|
- Add [Type conversions - From/Into](https://practice.rs/type-conversions/from-into.html)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
This book was designed for easily diving into and get skilled with Rust, and it's very easy to use: All you need to do is to make each exercise compile without ERRORS and Panics !
|
This book was designed for easily diving into and get skilled with Rust, and it's very easy to use: All you need to do is to make each exercise compile without ERRORS and Panics !
|
||||||
|
|
||||||
> 🎊 Updated on 2022-03-10: Add [Panics](https://practice.rs/result-panic/panic.html)
|
> 🎊 Updated on 2022-03-11: Add [Result and ?](https://practice.rs/result-panic/result.html)
|
||||||
|
|
||||||
## Reading online
|
## Reading online
|
||||||
|
|
||||||
|
|
|
@ -86,3 +86,16 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
6.
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let mut v = String::from("hello,");
|
||||||
|
let r = &mut v;
|
||||||
|
|
||||||
|
match r {
|
||||||
|
// The type of value is &mut String
|
||||||
|
value => value.push_str(" world!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -0,0 +1,177 @@
|
||||||
|
1.
|
||||||
|
```rust
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
|
||||||
|
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, Ok(20));
|
||||||
|
|
||||||
|
let result = multiply("4", "2");
|
||||||
|
assert_eq!(result.unwrap(), 8);
|
||||||
|
|
||||||
|
println!("Success!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2.
|
||||||
|
```rust
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
// IMPLEMENT multiply with ?
|
||||||
|
// DON'T use unwrap here
|
||||||
|
fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
|
||||||
|
let n1 = n1_str.parse::<i32>()?;
|
||||||
|
let n2 = n2_str.parse::<i32>()?;
|
||||||
|
Ok(n1 * n2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert_eq!(multiply("3", "4").unwrap(), 12);
|
||||||
|
println!("Success!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3.
|
||||||
|
```rust
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_file2() -> Result<String, io::Error> {
|
||||||
|
let mut s = String::new();
|
||||||
|
|
||||||
|
File::open("hello.txt")?.read_to_string(&mut s)?;
|
||||||
|
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert_eq!(read_file1().unwrap_err().to_string(), read_file2().unwrap_err().to_string());
|
||||||
|
println!("Success!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4.
|
||||||
|
```rust
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
fn add_two(n_str: &str) -> Result<i32, ParseIntError> {
|
||||||
|
n_str.parse::<i32>().map(|num| num +2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert_eq!(add_two("4").unwrap(), 6);
|
||||||
|
|
||||||
|
println!("Success!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
fn add_two(n_str: &str) -> Result<i32, ParseIntError> {
|
||||||
|
n_str.parse::<i32>().and_then(|num| Ok(num +2))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert_eq!(add_two("4").unwrap(), 6);
|
||||||
|
|
||||||
|
println!("Success!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
5.
|
||||||
|
```rust
|
||||||
|
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 MUST USING `and_then` and `map` here
|
||||||
|
fn multiply1(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
|
||||||
|
// IMPLEMENT...
|
||||||
|
n1_str.parse::<i32>().and_then(|n1| {
|
||||||
|
n2_str.parse::<i32>().map(|n2| n1 * n2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
6.
|
||||||
|
```rust
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
// Define a generic alias for a `Result` with the error type `ParseIntError`.
|
||||||
|
type Res<T> = Result<T, ParseIntError>;
|
||||||
|
|
||||||
|
// 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"));
|
||||||
|
}
|
||||||
|
```
|
|
@ -38,7 +38,7 @@
|
||||||
- [Others](type-conversions/others.md)
|
- [Others](type-conversions/others.md)
|
||||||
- [Result and panic](result-panic/intro.md)
|
- [Result and panic](result-panic/intro.md)
|
||||||
- [panic!](result-panic/panic.md)
|
- [panic!](result-panic/panic.md)
|
||||||
- [result and ?](result-panic/result.md)
|
- [Result and ?](result-panic/result.md)
|
||||||
- [Crate and module TODO](crate-module/intro.md)
|
- [Crate and module TODO](crate-module/intro.md)
|
||||||
- [Crate](crate-module/crate.md)
|
- [Crate](crate-module/crate.md)
|
||||||
- [Module](crate-module/module.md)
|
- [Module](crate-module/module.md)
|
||||||
|
|
|
@ -101,4 +101,19 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
6. 🌟🌟 Using pattern `&mut V` to match a mutable reference needs you to be very careful due to `V` being a value after matching
|
||||||
|
```rust,editable
|
||||||
|
|
||||||
|
// FIX the error with least changing
|
||||||
|
// DON'T remove any code line
|
||||||
|
fn main() {
|
||||||
|
let mut v = String::from("hello,");
|
||||||
|
let r = &mut v;
|
||||||
|
|
||||||
|
match r {
|
||||||
|
&mut value => value.push_str(" world!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
````
|
||||||
|
|
||||||
> You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it
|
> You can find the solutions [here](https://github.com/sunface/rust-by-practice)(under the solutions path), but only use it when you need it
|
|
@ -1 +1,212 @@
|
||||||
# result and ?
|
# 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(())
|
||||||
|
}
|
||||||
|
```
|
|
@ -97,4 +97,19 @@ fn main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
6. 🌟🌟 使用模式 `&mut V` 去匹配一个可变引用时,你需要格外小心,因为匹配出来的 `V` 是一个值,而不是可变引用
|
||||||
|
```rust,editable
|
||||||
|
|
||||||
|
// 修复错误,尽量少地修改代码
|
||||||
|
// 不要移除任何代码行
|
||||||
|
fn main() {
|
||||||
|
let mut v = String::from("hello,");
|
||||||
|
let r = &mut v;
|
||||||
|
|
||||||
|
match r {
|
||||||
|
&mut value => value.push_str(" world!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
````
|
||||||
|
|
||||||
> 你可以在[这里](https://github.com/sunface/rust-by-practice)找到答案(在 solutions 路径下)
|
> 你可以在[这里](https://github.com/sunface/rust-by-practice)找到答案(在 solutions 路径下)
|
Loading…
Reference in New Issue