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(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:?}");
}

? 演算子は、関数の戻り値の型と互換性のある値を返さなければなりません。Result の場合、これはエラー型に互換性が必要であることを意味します。Result<T, ErrorOuter> を返す関数が Result<U, ErrorInner> 型の値に対して ? を使えるのは、ErrorOuterErrorInner が同じ型であるか、ErrorOuterFrom<ErrorInner> を実装している場合に限られます。

From 実装の一般的な代替手段は Result::map_err であり、特に変換が 1 か所でしか発生しない場合によく使われます。

Option には互換性要件がありません。Option<T> を返す関数は、任意の TU の型について Option<U> に対して ? 演算子を使用できます。

Result を返す関数は Option に対して ? を使えず、その逆も同様です。ただし、Option::ok_orOptionResult に変換し、Result::okResultOption に変換します。