mirror of
https://github.com/darkicewolf50/RustBrock.git
synced 2025-06-15 13:04:18 -06:00
finshed ch9.2 started ch9.3
This commit is contained in:
parent
03f554f077
commit
67f7477663
@ -482,3 +482,142 @@ To fix this error we have two options:
|
||||
|
||||
The eror message also mentioned that `?` can be used with the `Option<T>` values as well
|
||||
|
||||
The behavior when `?` is called with an `Option<T>` is the same as `Result<T, E>`
|
||||
|
||||
If the value is `None`, this will be returned early as `None`
|
||||
|
||||
If the value is `Some`, the value inside the `Some` is the resultant value of the expression and the function continues
|
||||
|
||||
Here is an example where the function finds the last character of the first line in the given text
|
||||
```rust
|
||||
fn last_char_of_first_line(text: &str) -> Option<char> {
|
||||
text.lines().next()?.chars().last()
|
||||
}
|
||||
```
|
||||
|
||||
This function returns `Option<char>` because its possible that there is a character there, but it is also possible that there is not
|
||||
|
||||
This code taskes a string slice and calls the `lines` method on it, which returns an iterator over the lines in the string. Becasue this function wants to examine the first line, it calls `next` on the iterator to get the first value from the iterator
|
||||
|
||||
If the string is emtpy then `next` will return `None`, if this is the case then yo can use `?` to stop and return `None` form `last_char_of_first_line`
|
||||
|
||||
If its not then `next` will return a `Some` value that contains a string slice of the first line in the string slice argument
|
||||
|
||||
The `?` extracts the string slice, then we can call `chars` on that string slice to get an iterator of its characters.
|
||||
|
||||
Since we want the last character in this first line, so we call `last` to return the last item in the iterator
|
||||
|
||||
This is an `Option` becasue it is possible that the first line is empty string
|
||||
|
||||
For example the string slice could contain `"\nhi"`
|
||||
|
||||
If there is a value then a `Some` will be returned and the `?` in this case would not be needed due to it being the last part of the function and it would do essentially nothing
|
||||
|
||||
Note that ou can use the `?` on a `Result` in a fuction that returns `Reuslt` and you can do the same on a function that returns a `Option`
|
||||
|
||||
You cannot mix and match
|
||||
|
||||
The `?` operator won't automatically convert a `Result` to an `Option` or vice versa
|
||||
|
||||
In those cases you can use methods like the `ok` method on `Result` or the `ok_or` method on `Option` to do the conversion explicitly
|
||||
|
||||
If you can implement this kind of logic then you would need to use other method calls or a `match` expression
|
||||
|
||||
Main can either return a `()` or one of the possibilities is to return a `Result<(), E>`
|
||||
|
||||
Here is a concerte example that return a `Result<(), Box<dyn Error>>` and added a return value `Ok(())` to the end
|
||||
|
||||
This ensures that the code will compile
|
||||
```rust
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let greeting_file = File::open("hello.txt")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
The `Box<dyn Error>` type is a trait object, which for now means "any kind of error"
|
||||
|
||||
Using v`?` on a `Result` value in with the error type `Box<dyn Error>` is allowed becuase it allows any `Err` value to be returned early.
|
||||
|
||||
Even though the body of this `main` function will only ever return errors of the type `std::io::Eror` by specifying `Box<dyn Error>`, this will continue to be correct even if more code that returns other errors is added to the body of `main`
|
||||
|
||||
When a `main` returns a `Result<(), E>` the executable will exit with a value of `0` if `main` returns `Ok(())` and will return a nonzero value if `main` returns an `Err` value.
|
||||
|
||||
In C executalbes return a integer when they exit, programs that successfully return the `0` integer and reutrn an interger other than `0`
|
||||
|
||||
Rust also returns integers form executables to be compatible with this convention
|
||||
|
||||
`main` can return any types that implement the [`std::proccess::Termination`](https://doc.rust-lang.org/std/process/trait.Termination.html) trait which contains a function `report` that returns an `ExitCode`
|
||||
|
||||
See the std library documentation for more info on implementing the `Termination` trait for custom types developed by you
|
||||
|
||||
## To panic! or Not to panic!
|
||||
When to call `panic!` and when to return `Result`
|
||||
|
||||
When the code panics there is no way to recover
|
||||
|
||||
you could call `panic!` for any erro sutuation whether there is a possible way to recover or not but then you make the decision that it is completely unrecoverable on behalf of the code
|
||||
|
||||
Or you could return a `Result` value and give the calling code option to handle it or attempt to recover in a way that's appropriate for its situation or you could decide that an speicfic `Err` value is unrecoverable and choose to call a `panic!` to turn a recoverable one into an unrecoverable
|
||||
|
||||
This makes returning a `Result` is a good default cohice for when yo are defining a function that may fail
|
||||
|
||||
In situations like prototype code, and tests its more appropriate to write code that panics instead of returning a `Result`
|
||||
|
||||
### Examples, Prototype Code and Tests
|
||||
|
||||
When writing an example to illustrate some concept, also inclduing robust error-handling code can make examples less clear
|
||||
|
||||
In examples its clear that a call to a mthod like `unwrap` that could panic is meant as a placeholder for the way you want your app to handle erros
|
||||
|
||||
Similarly the `unwrap` and `expect` methods are vary handy when prototyping, before you are ready to decide how to hanlde errors.
|
||||
|
||||
This leaves clear markers in your code for when you are ready to make your app more robust
|
||||
|
||||
If a method call fails in a test, you want the whole test to fail, even if that method isnt the functionality under the test
|
||||
|
||||
`panic!` is how a test is marked as a failure, calling `unwrap` or `expect` is exactly what should happen
|
||||
|
||||
### Cases in Which You Have More Info Than the Compiler
|
||||
|
||||
Its appropraite to call `unwrap` or `expect` whne you have some other logic that enusres the `Result` will have an `Ok` value, ut the logic is not something the compiler understands
|
||||
|
||||
You can sometimes still have a `Result` value even though the value inside should always be `Ok`
|
||||
|
||||
This is the case whenever you call a function or operation has a possibility of failing in generally, even though it is logically imposible in your situation.
|
||||
|
||||
This case happens when you can manually inspect the code and you would never expect an `Err` variant
|
||||
|
||||
It is unacceptable to call `unwrap` in these situations, it is better to document the reason you think you would never have an `Err` varaint in the `expect` text
|
||||
|
||||
Here is an example
|
||||
```rust
|
||||
use std::net::IpAddr;
|
||||
|
||||
let home: IpAddr = "127.0.0.1"
|
||||
.parse()
|
||||
.expect("Hardcoded IP address should be valid");
|
||||
```
|
||||
|
||||
In this example it is creating an `IpAddr` instance by parsing a hard coded string
|
||||
|
||||
We can see that the string is a valid IP address, so it's acceptable to use `expect` here
|
||||
|
||||
Having a hardcoded, valid string doesnt change the return type of the `parse` method, we still get a `Result` value
|
||||
|
||||
The compiler will force us to handle the `Result` as if the `Err` value is a possiblity
|
||||
|
||||
This is because the compiler cannot see the logic from a hard coded string which doesnt have the possibility of failure
|
||||
|
||||
Mentioning the assumption that this IP address is hardcoded will prompt us to change `expect` to better error-handing code if in the future, we need to get the IP address from some other source instead
|
||||
|
||||
## Guidelines for Error Handling
|
||||
Its advisable to have your code panic when its possible to get ino a bad state
|
||||
|
||||
A *bad state* is when some assumption, guarantee, contract, or invariant has been broken such as when invalid values, contradictory values, or missing values are passed to your code plus one or more of the following:
|
||||
- The bad state is somthing that is unexpected which is not something that is a likely problem (a user entering data in the wrong format)
|
||||
- The code needs to rely on not being in a bad state, rather than checking at every step
|
Loading…
x
Reference in New Issue
Block a user