finshed ch9.2 started ch9.3

This commit is contained in:
darkicewolf50 2025-01-24 00:07:27 +00:00
parent 03f554f077
commit 67f7477663

View File

@ -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