Rust by Example

16.7.3 try!入門

前項の例では、unwrapの使用を避けようとしたところ、ネストがどんどん深くなっていってしまいましたが、本当の目的は変数を取り出すことだったはずです。では、panicせずにこのアプローチをよりコンパクトにする方法はあるのでしょうか?えーと、Errが見つかった時に取れる手法として有効なものには何があるでしょう?結論としては2つです。

  1. panic!、これは可能ならば避けるとすでに決めました。
  2. returnErrとは処理が不可能であることを意味するためです。

これこそまさにtry!が必要となる理由です。これは、値がErrの際、panicの代わりにreturnすることを除けば、unwrapほぼ1同一です。

use std::io::prelude::*; use std::fs::File; type Result<T> = std::result::Result<T, String>; // 下準備。ファイルを作成し文字列を書き込む fn setup() -> std::io::Result<()> { let mut a = try!(File::create("a")); try!(a.write_all(b"grape")); let mut b = try!(File::create("b")); b.write_all(b"fruit") } // それぞれのファイルから`Result`でラップされたデータを取得 fn get_data(path: &str) -> Result<String> { // `try`は値をアンラップするかエラーを返す let mut file = try!(File::open(path) // エラーを文字列に変換する必要があるのは今までと同様 .map_err(|err| err.to_string()) ); let mut contents = String::new(); // データを`contents`に読み込む try!(file.read_to_string(&mut contents) .map_err(|err| err.to_string()) ); Ok(contents) } // 2つのファイルの内容を結合させて新しい`Result`を作成。 fn concat(a: &str, b: &str) -> Result<String> { let (data_a, data_b) = (try!(get_data(a)), try!(get_data(b))); Ok(data_a + &data_b) } fn main() { // ここの返り値のResultは無視する setup().unwrap(); match concat("a", "b") { Ok(n) => println!("{}", n), Err(e) => println!("Error: {}", e), } }

これは本当に大きな改善ですが、map_errがうるさいという問題はまだ解決していません。使わずに済ませる方法はあるのですが(今は必要そうなところ全てで使用しています)、そのためにはまだ触れていない詳細に立ち入る必要があります。まずはより良いエラーの作り方を学んでいきましょう。

1. より詳しくはtry!再入門の項を見ましょう。

See also:

Resultio::Result