Rustのクエスチョンマーク

Rustに出現するクエスチョンマーク

let f = File::open("input.txt")?;

Rust 1.13で導入された機能で、ほぼ try! の構文糖衣である。

「ほぼ」というのは、 ? が将来的にはより汎用的に使えるように設計されているためで、 Result に限らない一般の std::ops:Carrier に対して動作する。

構文的には、 ? はメソッドチェーンと同じ優先度で解釈される。 (syntax::parse::parser 2495行目)

let j = a??.x?.f????().g().y??;
let j = ((((((((((((((a?)?).x)?).f)?))?)?)?)()).g()).y)?)?;

わかりやすく言うと ?try! の構文糖衣のようなものである。 try! の定義は以下の通り。(core::macros 309行目)

macro_rules! try {
    ($expr:expr) => (match $expr {
        $crate::result::Result::Ok(val) => val,
        $crate::result::Result::Err(err) => {
            return $crate::result::Result::Err($crate::convert::From::from(err))
        }
    })
}

ただし、 ? は Carrier による一般化を考慮に入れた実装になっている。 (rustc::hir::lowering 1762行目)

変換は処理系が行っている(AST→HIR変換時)が、同じものをマクロで書くなら以下の通り:

macro_rules! try {
    ($expr:expr) => (match $crate::ops::Carrier::translate($expr) {
        $crate::result::Result::Ok(val) => val,
        $crate::result::Result::Err(err) => {
            return $crate::ops::Carrier::from_error($crate::convert::From::from(err))
        }
    })
}

実質的には Carrier::translateT=Result<Self::Success, Self::Error> に対してのみ使われていて、一度 Result に変換して try! してまた Carrier に戻している動作に他ならない。

用途としては、効率化のために Result とは異なる構造を採用しているライブラリ(本当に効率化するのかは不明だが……)が Carrier を実装することで ? をサポートする、というのが考えられる。combine::ConsumedResult とか。

ただし、 Carrier による一般化は1.15.1時点では不安定機能に分類されている。