Перетворення спроб

Ефективне розширення ? є трохи складнішим, ніж було зазначено раніше:

expression?

працює так само, як

match expression {
    Ok(value) => value,
    Err(err)  => return Err(From::from(err)),
}

Виклик From::from тут означає, що ми намагаємося перетворити тип помилки на тип, який повертає функція. Це дозволяє легко інкапсулювати помилки у помилки вищого рівня.

Приклад

use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::fs::File;
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 {
            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);
    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:?}");
}
This slide should take about 5 minutes.

Оператор ? повинен повертати значення, сумісне з типом повернення функції. Для 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.