2023-11-29 10:39:24 -05:00
|
|
|
---
|
|
|
|
minutes: 5
|
|
|
|
---
|
|
|
|
|
|
|
|
# Try Conversions
|
|
|
|
|
2023-12-31 00:15:07 +01:00
|
|
|
The effective expansion of `?` is a little more complicated than previously
|
|
|
|
indicated:
|
2023-11-29 10:39:24 -05:00
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
expression?
|
|
|
|
```
|
|
|
|
|
|
|
|
works the same as
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
match expression {
|
|
|
|
Ok(value) => value,
|
|
|
|
Err(err) => return Err(From::from(err)),
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The `From::from` call here means we attempt to convert the error type to the
|
|
|
|
type returned by the function. This makes it easy to encapsulate errors into
|
|
|
|
higher-level errors.
|
|
|
|
|
|
|
|
## Example
|
2023-02-03 10:12:31 +00:00
|
|
|
|
|
|
|
```rust,editable
|
|
|
|
use std::error::Error;
|
|
|
|
use std::fmt::{self, Display, Formatter};
|
2023-12-20 07:51:20 -08:00
|
|
|
use std::fs::File;
|
2023-02-03 10:12:31 +00:00
|
|
|
use std::io::{self, Read};
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum ReadUsernameError {
|
|
|
|
IoError(io::Error),
|
|
|
|
EmptyUsername(String),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Error for ReadUsernameError {}
|
|
|
|
|
|
|
|
impl Display for ReadUsernameError {
|
|
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
|
|
match self {
|
2023-04-06 06:00:10 -04:00
|
|
|
Self::IoError(e) => write!(f, "IO error: {e}"),
|
2023-12-31 00:15:07 +01:00
|
|
|
Self::EmptyUsername(path) => write!(f, "Found no username in {path}"),
|
2023-02-03 10:12:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<io::Error> for ReadUsernameError {
|
2023-12-20 07:51:20 -08:00
|
|
|
fn from(err: io::Error) -> Self {
|
|
|
|
Self::IoError(err)
|
2023-02-03 10:12:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_username(path: &str) -> Result<String, ReadUsernameError> {
|
|
|
|
let mut username = String::with_capacity(100);
|
|
|
|
File::open(path)?.read_to_string(&mut username)?;
|
|
|
|
if username.is_empty() {
|
|
|
|
return Err(ReadUsernameError::EmptyUsername(String::from(path)));
|
|
|
|
}
|
|
|
|
Ok(username)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
//fs::write("config.dat", "").unwrap();
|
|
|
|
let username = read_username("config.dat");
|
|
|
|
println!("username or error: {username:?}");
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
<details>
|
|
|
|
|
2023-12-20 07:51:20 -08:00
|
|
|
The `?` operator must return a value compatible with the return type of the
|
2023-12-31 00:15:07 +01:00
|
|
|
function. For `Result`, it means that the error types have to be compatible. A
|
|
|
|
function that returns `Result<T, ErrorOuter>` can only use `?` on a value of
|
2023-12-20 07:51:20 -08:00
|
|
|
type `Result<U, ErrorInner>` if `ErrorOuter` and `ErrorInner` are the same type
|
|
|
|
or if `ErrorOuter` implements `From<ErrorInner>`.
|
2023-11-29 10:39:24 -05:00
|
|
|
|
2023-12-31 00:15:07 +01:00
|
|
|
A common alternative to a `From` implementation is `Result::map_err`, especially
|
|
|
|
when the conversion only happens in one place.
|
2023-02-03 10:12:31 +00:00
|
|
|
|
2023-12-20 07:51:20 -08:00
|
|
|
There is no compatibility requirement for `Option`. A function returning
|
|
|
|
`Option<T>` can use the `?` operator on `Option<U>` for arbitrary `T` and `U`
|
|
|
|
types.
|
2023-02-03 10:12:31 +00:00
|
|
|
|
2023-12-20 07:51:20 -08:00
|
|
|
A function that returns `Result` cannot use `?` on `Option` and vice versa.
|
|
|
|
However, `Option::ok_or` converts `Option` to `Result` whereas `Result::ok`
|
|
|
|
turns `Result` into `Option`.
|
2023-11-29 10:39:24 -05:00
|
|
|
|
2023-02-03 10:12:31 +00:00
|
|
|
</details>
|