Преобразования через Try
Фактическое раскрытие ? немного сложнее, чем показано ранее:
// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0
expression?
работает так же, как
// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0
match expression {
Ok(value) => value,
Err(err) => return Err(From::from(err)),
}
Вызов From::from здесь означает, что мы пытаемся преобразовать тип ошибки в тип, возвращаемый функцией. Это упрощает инкапсуляцию ошибок в более высокоуровневые ошибки.
Пример
// 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,
}
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);
}
Ok(username)
}
fn main() {
//std::fs::write("config.dat", "").unwrap();
let username = read_username("config.dat");
println!("username or error: {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.