contiuning ch9.2

This commit is contained in:
darkicewolf50 2025-01-22 23:54:06 +00:00
parent a6eeda7f49
commit fc4ae243cb

View File

@ -173,5 +173,148 @@ This return type indicates that call may succeed and reutnr a file handle that w
`File::open` function needs a way to tell us whether it succeeded or failed hence the use of `Result` enum which conveys this message of failure or success
When `File::open` succeeds, the value in `greeting_file_result` will be an instance of `Ok` that contains a file handle
When it fails the value in `greeting_file_result` will be an intance of `Er` that contains mroe info about the kind o error that occurred
When it fails the value in `greeting_file_result` will be an intance of `Er` that contains more info about the kind of error that occurred
We need to add a match in order to use `Result` here is one option
```rust
use std::fs::File;
fn main() {
let greeting_file_result = File::open("hello.txt");
let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {error:?}"),
};
}
```
This is like the `Option` enum
`Result` enum and its variants have been brought into scope by the prelude, so you dont need to specifiy `Result::` before the `Ok` and `Err` variants in the match arm
When the result is `Ok` the code will reutrn the `file` value out itself and then we can assign that file handle to the `greeting_file`. After machh we can use the file for reading or writing.
The other arm in `match` handles the `Err` value we get from `File::open`, in this case we choose to call the `panic!` macro with more details about the error
### Matching on Different Errors
You can take different actions depending on the type of failure
For example if a file doesnt open because it doesnt exist then maybe you want you want to create the file first instead of panicing and exiting the program
One way we can do this is by using a `match` for the returned value `Result<T, E>`
Example
```rust
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let greeting_file_result = File::open("hello.txt");
let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Problem creating the file: {e:?}"),
},
other_error => {
panic!("Problem opening the file: {other_error:?}");
}
},
};
}
```
The tpye that ``File::open` reutrn inside thr `Err` varian is `io:Error`, this is a struct provided by the std library
This struct has a method `kind` which can be called to get an `io::ErrorKind` value
The enum `io::ErrorKind` is also provided by a std library which has vairants representing the different kinds of errors that may result from an `io` operation
The error we care about is `Error::NotFound`, this indicates that the file that we try to open doesnt exist.
In this case we need to have a match on the outher thne use that on an inner match on `error.kind()`
In this inner match we create a file which can also fail hence in 2nd inner match to each return a file handle or panic out of the program
### Alternatives to Using match with `Result<T, E>`
this match expression is very useful but also primitive and boilerplate
one example to condese this code is
```rust
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let greeting_file = File::open("hello.txt").unwrap_or_else(|error| {
if error.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("Problem creating the file: {error:?}");
})
} else {
panic!("Problem opening the file: {error:?}");
}
});
}
```
This accomplishes the same thing as the the example above but it uses if and else and the `unwrap_or_else`
this is much cleaner to read even though it odesnt use and match statements
look at the exact definition `unwrap_or_else` in the std library document
### Shortcuts for Panic on Error: unwrap and expect
`match` works well, but it can be a bit verbose and doesnt communicate clearly what the intent is
`Result<T, E>` has many helper methods to do specific tasks
The `unwrap` method that is a shortcut for the `match` expression that was used before
If the `Result` value is a `Ok` variant, `unwrap` will return the value inside the `Ok`
If the `Result` is the `Err` variant, `unwrap` will call the `panc!` macro for us
Here is an example of its use
```rust
use std::fs::File;
fn main() {
let greeting_file = File::open("hello.txt").unwrap();
}
```
If this code without a *hello.txt* it will panic with an error like this
```
thread 'main' panicked at src/main.rs:4:49:
called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }
```
You can also use the `expect` method lets us also choose the `panic!` error message
This is good for providing clearer error mesages and make tracking down the source of the panic easier
Here is an example of an `expect` in use
```rust
use std::fs::File;
fn main() {
let greeting_file = File::open("hello.txt")
.expect("hello.txt should be included in this project");
}
```
`expect` is used in the same way as `unwrap` to retunr the file handle or call the `panic!` macro
The message is used by `expect` in its call to `panic!` as the paramter that is passed to `expect` rather than a defualt message
`expect` is maninly used in production-quality code over `unwrap` becasue it gives more context about why the opertaion is always expected to succeed
### Propagating Errors
When a operation fails you may want to pass back the error instead of handling the error within the function itself