almost done iwth ch9.2

This commit is contained in:
darkicewolf50 2025-01-23 19:03:21 +00:00
parent fc4ae243cb
commit 03f554f077

View File

@ -317,4 +317,168 @@ The message is used by `expect` in its call to `panic!` as the paramter that is
`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
When a operation fails you may want to pass back the error instead of handling the error within the function itself
This is used when you want the code that calls it to handle the error
here is an example is of this
```rust
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
let username_file_result = File::open("hello.txt");
let mut username_file = match username_file_result {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut username = String::new();
match username_file.read_to_string(&mut username) {
Ok(_) => Ok(username),
Err(e) => Err(e),
}
}
```
This could be written in an even smaller way, but this is good for exploration
For example is has a return type of `Result<String, io::Error>` this is also a vaule of `Result<T, E>` where the values are filled in with `String` and `io:Error`, also note thta the ownership will transfer
If the function succeeds without any problems then the cod that calls it will receive an `Ok` value that holds a `String` - the `username` that this function read from the file
If this function encounters any problems then the code that calls this will recive an `Err` value that holds an instance of `io::Error` that contains more info aobut what the problems were.
`io:Error` as the return type was chosne becuase that happens to be the tpye of the error value returned from both operations we're callin in this function's body that might fail these functions that could fail are `File::open` and `read_to_string` method.
the last term does not need the `return` keyword do to it being both the last item and a expression
its up to the calling code how to hanlde the `Err` and `Ok` value, for example it could call a `panic!` if it gets a `Err` value
This pattern of propagting errors is so commin in Rust that Rust provides the question mark operator `?` to make it easier
#### A Shorcut for Popagation Errors: the ? Operator
Here is an implementation that is a bit smaller but deos the same thing as above
```rust
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
let mut username_file = File::open("hello.txt")?;
let mut username = String::new();
username_file.read_to_string(&mut username)?;
Ok(username)
}
```
The `?` is placed adter a `Result` value is defined in almost the same way as the `match` expressions were dfeined in the previous example
If the `Result` value is an `Ok`, the value inside the `Ok` will get returned form this expression and it will continue
If the value is an `Err`, the `Err` will be returned from the whole function as if we had used the `return` keyword so that the error value gets propagated to te calling code
The difference between what `match` and what `?` does is that error values that have thr `?` operator called on them go through the `from` function, defined in thr `From` trait in the std library which is used to convert values fomr one type to another.
When `?` operator calls the `from` function, the error type is received is converted into the error type defined in the return type of the current function
This is useful when a function returns one error type to represent all the ways a functon might fail, even if parts might fail for many different reasons
In the context of the example, the `?` at the end of the `File::open` call will return the value inside an `Ok` to the variable `useranme_file`
If an error occurs the `?` operator will retunr early out of the whole function and give any `Err` value to the calling code.
This same thing applies to the `?` at the end of the `read_to_string` call
The `?` operartor eliminates a lot of boilerplate and make this function's implementation simpler
Here is an even shorter version of this code
```rust
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() -> Result<String, io::Error> {
let mut username = String::new();
File::open("hello.txt")?.read_to_string(&mut username)?;
Ok(username)
}
```
Instead of creating a veriable `username_file` it has been chained to the call to `read_to_string` directly from the result of `File::open("hello.txt")?`
We still need a `?` at the end of `read_to_string` call, which still allows it to return an `Ok` value containg `username` when both `File::open` and `read_to_string` succeed rather than returning errors
The functionality is still the same as the two examples above
Here is an even shorter veriosn ising `fs::read_to_string`
```rust
use std::fs;
use std::io;
fn read_username_from_file() -> Result<String, io::Error> {
fs::read_to_string("hello.txt")
}
```
Reading a file into a string is so common that the std library provides the convenient `fs::read_to_string` function that opens a file, creates a new `String`, reads the contents of the file which puts the contents into that `String`, and returns it
But this doesnt show how to explain ho wto use it
### Where The ? Operator Can Be Used
The `?` operator can only be used in functions whose return type is compatible with the value in the `?` is used on
Because the `?` is defined to perform an early return of a value out of the function, in the same manner as the `match` expression.
In the match case there was an arm that retunred an `Err(e)` value that is compatible with its return
You can use `?` operator in main due to another crate being able to be used in a different crate
This requires that the retun type is compatable, `()` is not compatable with `Result`
For example this is not compatable
```rust
use std::fs::File;
fn main() {
let greeting_file = File::open("hello.txt")?;
}
```
But it will outbut something like this
```
$ cargo run
Compiling error-handling v0.1.0 (file:///projects/error-handling)
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> src/main.rs:4:48
|
3 | fn main() {
| --------- this function should return `Result` or `Option` to accept `?`
4 | let greeting_file = File::open("hello.txt")?;
| ^ cannot use the `?` operator in a function that returns `()`
|
= help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
help: consider adding return type
|
3 ~ fn main() -> Result<(), Box<dyn std::error::Error>> {
4 | let greeting_file = File::open("hello.txt")?;
5 +
6 + Ok(())
7 + }
|
For more information about this error, try `rustc --explain E0277`.
error: could not compile `error-handling` (bin "error-handling") due to 1 previous error
```
If you notice this states that you are only allowed to use `?` if it returns `Result`, `Option` or another type that implements `FromResidual`
To fix this error we have two options:
1. Change your return tpye of your function to be compatible with the value yo're using the `?` operator on, so long as there are no other restrictions preventing this
1. Use a `match` or one of the `Result<T, E>` methos to handle the `Result<T, E>` in the may most appropriate
The eror message also mentioned that `?` can be used with the `Option<T>` values as well