src/error-handling/try-conversions.md
The effective expansion of ? is a little more complicated than previously
indicated:
# // Copyright 2023 Google LLC
# // SPDX-License-Identifier: Apache-2.0
#
expression?
works the same as
# // Copyright 2023 Google LLC
# // SPDX-License-Identifier: Apache-2.0
#
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.
# // Copyright 2023 Google LLC
# // SPDX-License-Identifier: Apache-2.0
#
use std::error::Error;
use std::io::Read;
use std::{fmt, fs, io};
#[derive(Debug)]
enum ReadUsernameError {
IoError(io::Error),
EmptyUsername(String),
}
impl Error for ReadUsernameError {}
impl fmt::Display for ReadUsernameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::IoError(e) => write!(f, "I/O error: {e}"),
Self::EmptyUsername(path) => write!(f, "Found no username in {path}"),
}
}
}
impl From<io::Error> for ReadUsernameError {
fn from(err: io::Error) -> Self {
Self::IoError(err)
}
}
fn read_username(path: &str) -> Result<String, ReadUsernameError> {
let mut username = String::with_capacity(100);
fs::File::open(path)?.read_to_string(&mut username)?;
if username.is_empty() {
return Err(ReadUsernameError::EmptyUsername(String::from(path)));
}
Ok(username)
}
fn main() {
//std::fs::write("config.dat", "").unwrap();
let username = read_username("config.dat");
println!("username or error: {username:?}");
}
The ? operator must return a value compatible with the return type of the
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
type Result<U, ErrorInner> if ErrorOuter and ErrorInner are the same type
or if ErrorOuter implements From<ErrorInner>.
A common alternative to a From implementation is Result::map_err, especially
when the conversion only happens in one place.
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.
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.