mirror of
https://github.com/darkicewolf50/RustBrock.git
synced 2025-06-15 13:04:18 -06:00
almost done iwth ch9.2
This commit is contained in:
parent
fc4ae243cb
commit
03f554f077
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user