mirror of
https://github.com/Dhghomon/easy_rust
synced 2024-11-15 18:13:23 +00:00
Rewrite Result
This commit is contained in:
parent
c2ebfbf145
commit
8310de02fb
110
README.md
110
README.md
@ -46,6 +46,7 @@ It is now late August, and *Easy Rust* is over 400 pages long. Easy Rust is almo
|
|||||||
- [References and the dot operator](#references-and-the-dot-operator)
|
- [References and the dot operator](#references-and-the-dot-operator)
|
||||||
- [Generics](#generics)
|
- [Generics](#generics)
|
||||||
- [Option and Result](#option-and-result)
|
- [Option and Result](#option-and-result)
|
||||||
|
- [Option](#option)
|
||||||
- [Result](#result)
|
- [Result](#result)
|
||||||
- [Other collections](#other-collections)
|
- [Other collections](#other-collections)
|
||||||
- [HashMap (and BTreeMap)](#hashmap-and-btreemap)
|
- [HashMap (and BTreeMap)](#hashmap-and-btreemap)
|
||||||
@ -3460,6 +3461,8 @@ We understand enums and generics now, so we can understand `Option` and `Result`
|
|||||||
|
|
||||||
We will start with `Option`.
|
We will start with `Option`.
|
||||||
|
|
||||||
|
### Option
|
||||||
|
|
||||||
You use `Option` when you have a value that might exist, or might not exist. When a value exists it is `Some(value)` and when it doesn't it's just `None`, Here is an example of bad code that can be improved with `Option`.
|
You use `Option` when you have a value that might exist, or might not exist. When a value exists it is `Some(value)` and when it doesn't it's just `None`, Here is an example of bad code that can be improved with `Option`.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
@ -3632,6 +3635,8 @@ Result is similar to Option, but here is the difference:
|
|||||||
- Option is about `Some` or `None` (value or no value),
|
- Option is about `Some` or `None` (value or no value),
|
||||||
- Result is about `Ok` or `Err` (okay result, or error result).
|
- Result is about `Ok` or `Err` (okay result, or error result).
|
||||||
|
|
||||||
|
So `Option` is if you are thinking: "Maybe there will be something, and maybe there won't." But `Result` is if you are thinking: "Maybe it will fail."
|
||||||
|
|
||||||
To compare, here are the signatures for Option and Result.
|
To compare, here are the signatures for Option and Result.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
@ -3648,25 +3653,70 @@ enum Result<T, E> {
|
|||||||
fn main() { }
|
fn main() { }
|
||||||
```
|
```
|
||||||
|
|
||||||
So Result has a value inside of `Ok`, and a value inside of `Err`. That is because errors usually have information inside them.
|
So Result has a value inside of `Ok`, and a value inside of `Err`. That is because errors usually (and should have) have information inside them.
|
||||||
|
|
||||||
`Result<T, E>` means you need to think of what you want to return for `Ok`, and what you want to return for `Err`. Actually, you can decide anything. Even this is okay:
|
`Result<T, E>` means you need to think of what you want to return for `Ok`, and what you want to return for `Err`. Actually, you can decide anything. Even this is okay:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
|
||||||
check_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_error() -> Result<(), ()> {
|
fn check_error() -> Result<(), ()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
check_error();
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`check_error` says "return `()` if we get `Ok`, and return `()` if we get `Err`". Then we return `Ok` with a `()`.
|
`check_error` says "return `()` if we get `Ok`, and return `()` if we get `Err`". Then we return `Ok` with a `()`.
|
||||||
|
|
||||||
Sometimes a function with Result will use a `String` for the `Err` value. This is not the best method to use, but sometimes it is okay.
|
The compiler gives us an interesting warning:
|
||||||
|
|
||||||
|
```text
|
||||||
|
warning: unused `std::result::Result` that must be used
|
||||||
|
--> src\main.rs:6:5
|
||||||
|
|
|
||||||
|
6 | check_error();
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(unused_must_use)]` on by default
|
||||||
|
= note: this `Result` may be an `Err` variant, which should be handled
|
||||||
|
```
|
||||||
|
|
||||||
|
This is true: we only returned the `Result` but it could have been an `Err`. So let's handle the error a bit, even though we're still not really doing anything.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
fn give_result(input: i32) -> Result<(), ()> {
|
||||||
|
if input % 2 == 0 {
|
||||||
|
return Ok(())
|
||||||
|
} else {
|
||||||
|
return Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if give_result(5).is_ok() {
|
||||||
|
println!("It's okay, guys")
|
||||||
|
} else {
|
||||||
|
println!("It's an error, guys")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This prints `It's an error, guys`. So we just handled our first error.
|
||||||
|
|
||||||
|
Remember, the four methods to easily check are `.is_some()`, `is_none()`, `is_ok()`, and `is_err()`.
|
||||||
|
|
||||||
|
|
||||||
|
Sometimes a function with Result will use a `String` for the `Err` value. This is not the best method to use, but it is a little better than what we've done so far.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn check_if_five(number: i32) -> Result<i32, String> {
|
||||||
|
match number {
|
||||||
|
5 => Ok(number),
|
||||||
|
_ => Err("Sorry, the number wasn't five.".to_string()), // This is our error message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut result_vec = Vec::new(); // Create a new vec for the results
|
let mut result_vec = Vec::new(); // Create a new vec for the results
|
||||||
|
|
||||||
@ -3676,13 +3726,6 @@ fn main() {
|
|||||||
|
|
||||||
println!("{:?}", result_vec);
|
println!("{:?}", result_vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_if_five(number: i32) -> Result<i32, String> {
|
|
||||||
match number {
|
|
||||||
5 => Ok(number),
|
|
||||||
_ => Err("Sorry, the number wasn't five.".to_string()), // This is our error message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Our vec prints:
|
Our vec prints:
|
||||||
@ -3695,6 +3738,7 @@ Err("Sorry, the number wasn\'t five.")]
|
|||||||
Just like Option, `.unwrap()` on `Err` will panic.
|
Just like Option, `.unwrap()` on `Err` will panic.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
// ⚠️
|
||||||
fn main() {
|
fn main() {
|
||||||
let error_value: Result<i32, &str> = Err("There was an error"); // Create a Result that is already an Err
|
let error_value: Result<i32, &str> = Err("There was an error"); // Create a Result that is already an Err
|
||||||
println!("{}", error_value.unwrap()); // Unwrap it
|
println!("{}", error_value.unwrap()); // Unwrap it
|
||||||
@ -3709,16 +3753,16 @@ thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: "There w
|
|||||||
|
|
||||||
This information helps you fix your code. `src\main.rs:30:20` means "inside main.rs in directory src, on line 30 and column 20". So you can go there to look at your code and fix the problem.
|
This information helps you fix your code. `src\main.rs:30:20` means "inside main.rs in directory src, on line 30 and column 20". So you can go there to look at your code and fix the problem.
|
||||||
|
|
||||||
You can also create your own error types. Result functions in the standard library usually have their own error types. For example:
|
You can also create your own error types. Result functions in the standard library and other people's code usually do this. For example, this function from the standard library:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// 🚧
|
// 🚧
|
||||||
pub fn from_utf8(vec: Vec<u8>) -> Result<String, FromUtf8Error>
|
pub fn from_utf8(vec: Vec<u8>) -> Result<String, FromUtf8Error>
|
||||||
```
|
```
|
||||||
|
|
||||||
This function take a vector of bytes (`u8`) and tries to make a `String`. So the success case for the Result is a `String` and the error case is `FromUtf8Error`. You can give your error type any name you want. We will create our own error types later, because first we need to learn other things.
|
This function take a vector of bytes (`u8`) and tries to make a `String`. So the success case for the Result is a `String` and the error case is `FromUtf8Error`. You can give your error type any name you want.
|
||||||
|
|
||||||
Using a `match` with Option and Result sometimes requires a lot of code. For example, the `.get()` method returns an Option on a Vec.
|
Using a `match` with `Option` and `Result` sometimes requires a lot of code. For example, the `.get()` method returns an `Option` on a `Vec`.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -3752,7 +3796,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This is good, but we don't do anything for `None`. Here we can make the code smaller by using `if let`. `if let` means "do something if it matches, and don't do anything if it doesn't". `if let` is when you don't care about matching for everything.
|
This is good, but we don't do anything for `None` because we don't care. Here we can make the code smaller by using `if let`. `if let` means "do something if it matches, and don't do anything if it doesn't". `if let` is when you don't care about matching for everything.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -3766,7 +3810,9 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`if let Some(number) = my_vec.get(index)` means "if you get `Some(number)` from `my_vec.get(index)`". Also note: it uses one `=`. It is not a boolean.
|
**Important to remember**: `if let Some(number) = my_vec.get(index)` means "if you get `Some(number)` from `my_vec.get(index)`".
|
||||||
|
|
||||||
|
Also note: it uses one `=`. It is not a boolean.
|
||||||
|
|
||||||
`while let` is like a while loop for `if let`. Imagine that we have weather station data like this:
|
`while let` is like a while loop for `if let`. Imagine that we have weather station data like this:
|
||||||
|
|
||||||
@ -3775,24 +3821,44 @@ fn main() {
|
|||||||
["Athens", "sunny", "not humid", "20", "10", "50"]
|
["Athens", "sunny", "not humid", "20", "10", "50"]
|
||||||
```
|
```
|
||||||
|
|
||||||
We want to get the numbers, but not the words. For the numbers, we can use a method called `parse::<i32>()`. `parse()` is the method, and `::<i32>` is the type. It will try to turn the `&str` into an `i32`, and give it to us if it can.
|
We want to get the numbers, but not the words. For the numbers, we can use a method called `parse::<i32>()`. `parse()` is the method, and `::<i32>` is the type. It will try to turn the `&str` into an `i32`, and give it to us if it can. It returns a `Result`, because it might not work (like if you wanted it to parse "Billybrobby" - that's not a number).
|
||||||
|
|
||||||
We will also use `.pop()`. This takes the last item off of the vector.
|
We will also use `.pop()`. This takes the last item off of the vector.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut weather_vec = vec!["Berlin", "cloudy", "5", "-7", "78"];
|
let weather_vec = vec![
|
||||||
while let Some(information) = weather_vec.pop() { // This means: keep going until you can't pop anymore
|
vec!["Berlin", "cloudy", "5", "-7", "78"],
|
||||||
|
vec!["Athens", "sunny", "not humid", "20", "10", "50"],
|
||||||
|
];
|
||||||
|
for mut city in weather_vec {
|
||||||
|
println!("For the city of {}:", city[0]); // In our data, every first item is the city name
|
||||||
|
while let Some(information) = city.pop() {
|
||||||
|
// This means: keep going until you can't pop anymore
|
||||||
// When the vector reaches 0 items, it will return None
|
// When the vector reaches 0 items, it will return None
|
||||||
// and it will stop.
|
// and it will stop.
|
||||||
if let Ok(number) = information.parse::<i32>() { // Try to parse the variable we called information
|
if let Ok(number) = information.parse::<i32>() {
|
||||||
|
// Try to parse the variable we called information
|
||||||
// This returns a result. If it's Ok(number), it will print it
|
// This returns a result. If it's Ok(number), it will print it
|
||||||
println!("The number is: {}", number);
|
println!("The number is: {}", number);
|
||||||
|
} // We don't write anything here because we do nothing if we get an error. Throw them all away
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This will print:
|
||||||
|
|
||||||
|
```text
|
||||||
|
For the city of Berlin:
|
||||||
|
The number is: 78
|
||||||
|
The number is: -7
|
||||||
|
The number is: 5
|
||||||
|
For the city of Athens:
|
||||||
|
The number is: 50
|
||||||
|
The number is: 10
|
||||||
|
The number is: 20
|
||||||
|
```
|
||||||
|
|
||||||
## Other collections
|
## Other collections
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user