Rustのtry-catch構文
Rustのnightlyに新しく入ったtry-catch関連構文を紹介する。
do catch
によるcatch構文
#![feature(catch_expr)] use std::fs::File; use std::io::{self, BufReader, Read}; fn main() { do catch { let f = File::open("foo.txt")?; let mut f = BufReader::new(f); let mut buf = String::new(); f.read_to_string(&mut buf)?; println!("{}", buf); Ok(()) }.unwrap_or_else(|err: io::Error| { eprintln!("An error occured: {}", err); }) }
catchはRust RFC 0243のtry-catch構文の一部として提案されていた。tryにあたるクエスチョンマークはRust1.13.0で安定化されたが、catchはまだ実装されていなかった。
catch構文は、これまた最近実装された値つき break
(Rust RFC 1624) の亜種を用いて脱糖される。すなわち、
do catch { do catch { ... e1? } e2? } e3?
のようになっている場合、これは
'catch1: { 'catch2: { ... match e2 { ... break 'catch2 err } } match e2 { ... break 'catch1 err } } match e3 { ... return err }
のようなHIRに脱糖される。ただし正確にはこのような構文は存在しない。ASTの構文上、ラベルをとることができるのは loop
, for
, while
のいずれかである。また、実際には 'catch1
のようなラベルが付与されるのではなく、NodeIdで直接break先が識別される。
なお、 do
は現時点では予約キーワードであり、 catch
は弱キーワード(文脈依存キーワード)となる。 catch
単体ではレコード構造体の初期化構文と紛らわしいため、 do
をつけることでそれを回避している。
?
の一般化
?
は現在の安定版では std::result::Result<T, E>
の処理だけができるようになっている。以前のnightlyではこれを std::ops::Carrier
というトレイトで一般化する実装が入っていたが、これは std::ops::Try
という別のトレイトに置き換えられることになった。(Rust RFC 1859)
それぞれのトレイトの定義は以下の通りである。
pub trait Carrier { type Success; type Error; fn from_success(_: Self::Success) -> Self; fn from_error(_: Self::Error) -> Self; fn translate<T>(self) -> T where T: Carrier<Success=Self::Success, Error=Self::Error>; } pub trait Try { type Ok; type Error; fn into_result(self) -> Result<Self::Ok, Self::Error>; fn from_error(v: Self::Error) -> Self; fn from_ok(v: Self::Ok) -> Self; }
名前のほかに異なる点として、 Carrier::translate
に存在していた多相性がなくなって、よりシンプルになっている。
これにより、 e?
は以下のように脱糖されることになる。
match ::std::ops::Try::into_result(e) { ::std::result::Result::Err(err) => #[allow(unreachable_code)] // do catch {} の内側の場合 break 'innermost_catch ::std::ops::Try::from_error(::std::convert::From::from(err)), // do catch {} の内側ではない場合 return ::std::ops::Try::from_error(::std::convert::From::from(err)), ::std::result::Result::Ok(val) => #[allow(unreachable_code)] val }
残念ながら現在の実装では Result
のみが Try
を実装している。