隱含轉換
比起先前提到的下列程式碼,?
的有效擴展稍微更複雜一點:
expression?
運作方式與以下程式碼相同:
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.
範例
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, "IO 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); 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:?}"); }
This slide should take about 5 minutes.
?
運算子必須傳回與函式傳回類型相容的值。如果是 Result
,表示錯誤類型必須相容。如果是傳回 Result<T, ErrorOuter>
的函式,當 ErrorOuter
和 ErrorInner
的型別相同、或者 ErrorOuter
實作 From<ErrorInner>
時,就只能在 Result<U, ErrorInner>
型別的值上使用 ?
。
From
實作的常見的替代方案是 Result::map_err
,特別是當轉換只在單一位置發生時更是如此。
Option
並沒有相容性規定。如果函式會傳回 Option<T>
,可以在 Option<U>
上將 ?
運算子用於任意的 T
和 U
型別。
傳回 Result
的函式無法在 Option
上使用 ?
,反之亦然。不過,Option::ok_or
會將 Option
轉換為 Result
,而 Result::ok
則將 Result
轉換為 Option
。