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::translate
は T=Result<Self::Success, Self::Error>
に対してのみ使われていて、一度 Result
に変換して try!
してまた Carrier
に戻している動作に他ならない。
用途としては、効率化のために Result
とは異なる構造を採用しているライブラリ(本当に効率化するのかは不明だが……)が Carrier
を実装することで ?
をサポートする、というのが考えられる。combine::ConsumedResult
とか。
ただし、 Carrier
による一般化は1.15.1時点では不安定機能に分類されている。