Перетворення спроб
Ефективне розширення ?
є трохи складнішим, ніж було зазначено раніше:
expression?
працює так само, як
match expression {
Ok(value) => value,
Err(err) => return Err(From::from(err)),
}
Виклик From::from
тут означає, що ми намагаємося перетворити тип помилки на тип, який повертає функція. Це дозволяє легко інкапсулювати помилки у помилки вищого рівня.
Приклад
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 помилка: {e}"), Self::EmptyUsername(path) => write!(f, "Не знайдено імені користувача в {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:?}"); }
Оператор ?
повинен повертати значення, сумісне з типом повернення функції. Для Result
це означає, що типи помилок мають бути сумісними. Функція, яка повертає Result<T, ErrorOuter>
, може використовувати ?
на значенні типу Result<U, ErrorInner>
тільки якщо ErrorOuter
і ErrorInner
мають однаковий тип або якщо ErrorOuter
реалізує From<ErrorInner>
.
Поширеною альтернативою реалізації From
є Result::map_err
, особливо коли перетворення відбувається лише в одному місці.
Для Option
немає вимог щодо сумісності. Функція, що повертає Option<T>
, може використовувати оператор ?
на Option<U>
для довільних типів T
та U
.
Функція, яка повертає Result
, не може використовувати ?
в Option
і навпаки. Однак, Option::ok_or
перетворює Option
в Result
, тоді як Result::ok
перетворює Result
в Option
.