diff --git a/1.6/ja/book/error-handling.md b/1.6/ja/book/error-handling.md index c4349fc5..c0e357d2 100644 --- a/1.6/ja/book/error-handling.md +++ b/1.6/ja/book/error-handling.md @@ -84,15 +84,15 @@ Rustでは戻り値を使います。 * [本当の `try!` マクロ](#本当の-try-マクロ) * [独自のエラー型を合成する](#独自のエラー型を合成する) * [ライブラリ作者たちへのアドバイス](#ライブラリ作者たちへのアドバイス) -* [ケーススタディ:人口データを読み込むプログラム](#case-study-a-program-to-read-population-data) - * [最初のセットアップ](#initial-setup) - * [引数のパース](#argument-parsing) - * [ロジックを書く](#writing-the-logic) - * [`Box` によるエラー処理](#error-handling-with-boxerror) - * [標準入力から読み込む](#reading-from-stdin) - * [独自のエラー型によるエラー処理](#error-handling-with-a-custom-type) - * [機能を追加する](#adding-functionality) -* [簡単な説明(まとめ)](#the-short-story) +* [ケーススタディ:人口データを読み込むプログラム](#ケーススタディ:人口データを読み込むプログラム) + * [最初のセットアップ](#最初のセットアップ) + * [引数のパース](#引数のパース) + * [ロジックを書く](#ロジックを書く) + * [`Box` によるエラー処理](#boxerror-によるエラー処理) + * [標準入力から読み込む](#標準入力から読み込む) + * [独自のエラー型によるエラー処理](#独自のエラー型によるエラー処理) + * [機能を追加する](#機能を追加する) +* [まとめ](#まとめ) # 基礎 @@ -246,7 +246,7 @@ fn find(haystack: &str, needle: char) -> Option { この関数がマッチする文字を見つけたとき、単に `offset` を返すだけではないことに注目してください。 その代わりに `Some(offset)` を返します。 -`Some` は `Option` 型の *値コンストラクタ* の一つです。 +`Some` は `Option` 型のヴァリアントの一つ、つまり *値コンストラクタ* です。 これは `fn(value: T) -> Option` という型の関数だと考えることもできます。 これに対応して `None` もまた値コンストラクタですが、こちらには引数がありません。 `None` は `fn() -> Option` という型の関数だと考えることもできます。 @@ -1515,7 +1515,7 @@ trait Error: Debug + Display { このトレイトは極めて一般的です。 なぜなら、エラーを表す *全て* の型で実装されることを目的としているからです。 -この後すぐ見るように、このことが合成可能なコードを書くのに間違いなく役立ちます。 +この後すぐ見るように、このことは合成可能なコードを書くのに間違いなく役立ちます。 それ以外にも、このトレイトでは最低でも以下のようなことができます: @@ -1568,8 +1568,8 @@ enum CliError { -このエラー型は2種類のエラー、つまり、IOを扱っているときのエラー、または、文字列を通知に変換するときのエラーが起こる可能性を示しています。 -`enum` 定義のバリアントを増やせば、エラーの種類をいくらでも表現できます。 +このエラー型は2種類のエラー、つまり、IOを扱っているときのエラー、または、文字列を数値に変換するときのエラーが起こる可能性を示しています。 +`enum` 定義のヴァリアントを増やせば、エラーの種類をいくらでも表現できます。 @@ -1651,8 +1651,8 @@ trait From { 嬉しいくらい簡単でしょ? -`From` は、ある特定の型 `T` から違う型へ変換するための汎用的な方法を提供するので大変便利です -(この場合の「違う型」とは実装の対象、つまり `Self` です)。 +`From` は、ある特定の `T` という型 *から* 、別の型へ変換するための汎用的な方法を提供するので大変便利です +(この場合の「別の型」とは実装の主体、つまり `Self` です)。 `From` を支えているのは [標準ライブラリで提供される一連の実装です](../std/convert/trait.From.html)。 @@ -1668,7 +1668,7 @@ let cow: ::std::borrow::Cow = From::from("foo"); たしかに `From` が文字列を変換するのに便利なことはわかりました。 でもエラーについてはどうでしょうか? -結論から言うと、これが最も重要な実装です: +結論から言うと、これが重要な実装です: ```rust,ignore impl<'a, E: Error + 'a> From for Box @@ -1713,8 +1713,8 @@ let err2: Box = From::from(parse_err); ここに気づくべき、本当に重要なパターンがあります。 `err1` と `err2` の両方ともが *同じ型* になっているのです。 -なぜなら、それらが存在量型、つまり、トレイトオブジェクトだからです。 -特にそれらの背後の型は、コンパイラーの知識から *消去されます* ので、 `err1` と `err2` が本当に同じに見えるのです。 +なぜなら、それらが存在量化型、つまり、トレイトオブジェクトだからです。 +特にそれらの背後の型は、コンパイラの知識から *消去されます* ので、 `err1` と `err2` が本当に同じに見えるのです。 さらに私たちは同じ関数呼び出し `From::from` を使って `err1` と `err2` をコンストラクトしました。 これは `From::from` が引数とリターン型の両方でオーバーロードされているからです。 @@ -1789,7 +1789,7 @@ fn file_double>(file_path: P) -> Result { 以前 `map_err` の呼び出しを取り除くことができると約束しました。 -もちろんです。ここでしなければいけないのは `From` と共に動く型を一つ選ぶことです。 +もちろんです。ここでしなければいけないのは `From` と共に動く型を一つ選ぶだけでよいのです。 前の節で見たように `From` の実装の一つは、どんなエラー型でも `Box` に変換できます: ```rust @@ -1855,7 +1855,7 @@ fn file_double>(file_path: P) -> Result> { -最後の説では `try!` マクロの本当の定義を確認し、それが `From::from` をエラーの値に対して呼ぶことで、自動的な型変換をする様子を見ました。 +最後の節では `try!` マクロの本当の定義を確認し、それが `From::from` をエラーの値に対して呼ぶことで、自動的な型変換をする様子を見ました。 特にそこでは、エラーを `Box` に変換しました。 これはたしかに動きますが、呼び出し元にとって、型がオペークになってしまいました。 @@ -1924,7 +1924,7 @@ impl From for CliError { -これらの実装がしていることは、`From` に対して、どうやって他のエラー型を元に `CliError` を作るのかを教えてあげることです。 +これらの実装がしていることは、`From` に対して、どうやって他のエラー型を元に `CliError` を作るのか、教えてあげているだけです。 このケースでは、単に対応する値コンストラクタを呼ぶことで構築しています。 本当に *普通は* これくらい簡単にできてしまいます。 @@ -1966,7 +1966,7 @@ fn file_double>(file_path: P) -> Result { -もし `file_double` 関数を変更して、なにか他の操作、例えば、文字列を浮動小数点数に変換させたいと思ったら、エラー型のバリアントを追加するだけです: +もし `file_double` 関数を変更して、なにか他の操作、例えば、文字列を浮動小数点数に変換させたいと思ったら、エラー型のヴァリアントを追加するだけです: ```rust use std::io; @@ -2046,38 +2046,56 @@ impl From for CliError { 特にライブラリでエラー型を一つだけ定義しているときは当てはまります。 この方法は標準ライブラリの [`io::Result`](../std/io/type.Result.html) や [`fmt::Result`](../std/fmt/type.Result.html) で用いられています。 -# Case study: A program to read population data - -This chapter was long, and depending on your background, it might be -rather dense. While there is plenty of example code to go along with -the prose, most of it was specifically designed to be pedagogical. So, -we're going to do something new: a case study. - -For this, we're going to build up a command line program that lets you -query world population data. The objective is simple: you give it a location -and it will tell you the population. Despite the simplicity, there is a lot -that can go wrong! - -The data we'll be using comes from the [Data Science -Toolkit][11]. I've prepared some data from it for this exercise. You -can either grab the [world population data][12] (41MB gzip compressed, -145MB uncompressed) or just the [US population data][13] (2.2MB gzip -compressed, 7.2MB uncompressed). - -Up until now, we've kept the code limited to Rust's standard library. For a real -task like this though, we'll want to at least use something to parse CSV data, -parse the program arguments and decode that stuff into Rust types automatically. For that, we'll use the -[`csv`](https://crates.io/crates/csv), -and [`rustc-serialize`](https://crates.io/crates/rustc-serialize) crates. - -## Initial setup - -We're not going to spend a lot of time on setting up a project with -Cargo because it is already covered well in [the Cargo -chapter](../book/hello-cargo.html) and [Cargo's documentation][14]. - -To get started from scratch, run `cargo new --bin city-pop` and make sure your -`Cargo.toml` looks something like this: + +# ケーススタディ:人口データを読み込むプログラム + + + + + +この章は長かったですね。 +あなたのバックグラウンドにもよりますが、内容が少し濃すぎたかもしれません。 +たくさんのコード例に、散文的な説明が添えられる形で進行しましたが、これは主に学習を助けるために、あえてこう構成されていたのでした。 +次はなにか新しいことをしましょう。ケーススタディです。 + + + + + +ここでは世界の人口データを問い合わせるための、コマンドラインプログラムを構築します。 +目標はシンプルです:プログラムに場所を与えると、人口を教えてくれます。 +シンプルにも関わらず、失敗しそうな所がたくさんあります! + + + + + + +ここで使うデータは [データサイエンスツールキット][11] から取得したものです。 +これを元に演習で使うデータを準備しましたので、2つのファイルのどちらかをダウンロードしてください: +[世界の人口データ][12] (gzip圧縮時 41MB、解凍時 145MB)と、 [アメリカ合衆国の人口データ][13] (gzip 圧縮時 2.2MB、解凍時 7.2MB)があります。 + + + + + + +いままで書いてきたコードでは、Rustの標準ライブラリだけを使うようにしてきました。 +今回のような現実のタスクでは、最低でもCSVデータをパースする部分と、プログラムの引数をパースして、自動的にRustの型にデコードする部分に何か使いたいでしょう。 +これには [`csv`](https://crates.io/crates/csv) と [`rustc-serialize`](https://crates.io/crates/rustc-serialize) クレートを使います。 + + +## 最初のセットアップ + + + + + +Cargoを使ってプロジェクトをセットアップしますが、その方法はすでに [Hello, Cargo!](../book/getting-started.html#hello-cargo) と [Cargoのドキュメント][14] でカバーされていますので、ここでは簡単に説明します。 + + + +何もない状態から始めるには、`cargo new --bin city-pop` を実行し、 `Cargo.toml` を以下のように編集します: ```text [package] @@ -2094,26 +2112,35 @@ rustc-serialize = "0.*" getopts = "0.*" ``` -You should already be able to run: + +これでもう実行できるはずです: ```text cargo build --release ./target/release/city-pop -# Outputs: Hello, world! -``` - -## Argument parsing - -Let's get argument parsing out of the way. We won't go into too much -detail on Getopts, but there is [some good documentation][15] -describing it. The short story is that Getopts generates an argument -parser and a help message from a vector of options (The fact that it -is a vector is hidden behind a struct and a set of methods). Once the -parsing is done, we can decode the program arguments into a Rust -struct. From there, we can get information about the flags, for -instance, whether they were passed in, and what arguments they -had. Here's our program with the appropriate `extern crate` -statements, and the basic argument setup for Getopts: +# 出力: Hello, world! +``` + + + +## 引数のパース + + + + + + + + + + + +引数のパースができるようにしましょう。 +Getoptsについては、あまり深く説明しませんが、詳細を解説した [ドキュメント][15] があります。 +簡単に言うと、Getoptsはオプションのベクタから、引数のパーサーとヘルプメッセージを生成します(実際には、ベクタは構造体とメソッドの背後に隠れています)。 +パースが終わると、プログラムの引数をRustの構造体へとデコードできます。 +そこから、例えば、フラグが指定されたかとか、フラグの引数がなんであったかといった、フラグの情報を取り出せるようになります。 +プログラムに適切な `extern crate` 文を追加して、Getoptsの基本的な引数を設定すると、こうなります: ```rust,ignore extern crate getopts; @@ -2144,40 +2171,66 @@ fn main() { let data_path = args[1].clone(); let city = args[2].clone(); - // Do stuff with information +# // Do stuff with information + // 情報を元にいろいろなことをする } ``` -First, we get a vector of the arguments passed into our program. We -then store the first one, knowing that it is our program's name. Once -that's done, we set up our argument flags, in this case a simplistic -help message flag. Once we have the argument flags set up, we use -`Options.parse` to parse the argument vector (starting from index one, -because index 0 is the program name). If this was successful, we -assign matches to the parsed object, if not, we panic. Once past that, -we test if the user passed in the help flag, and if so print the usage -message. The option help messages are constructed by Getopts, so all -we have to do to print the usage message is tell it what we want it to -print for the program name and template. If the user has not passed in -the help flag, we assign the proper variables to their corresponding -arguments. - -## Writing the logic - -We all write code differently, but error handling is usually the last thing we -want to think about. This isn't great for the overall design of a program, but -it can be useful for rapid prototyping. Because Rust forces us to be explicit -about error handling (by making us call `unwrap`), it is easy to see which -parts of our program can cause errors. - -In this case study, the logic is really simple. All we need to do is parse the -CSV data given to us and print out a field in matching rows. Let's do it. (Make -sure to add `extern crate csv;` to the top of your file.) +> 訳注 +> +> - Usage: {} [options] :使用法:{} [options] +> - Show this usage message.:この使用法のメッセージを表示する。 + + + + + + + + + + + + + + +このように、まず、このプログラムに渡された引数のベクタを取得します。 +次に、最初の要素、つまり、プログラムの名前を格納します。 +続いて引数フラグをセットアップしますが、今回はごく簡単なヘルプメッセージフラグが一つあるだけです。 +セットアップできたら `Options.parse` を使って引数のベクタをパースします(インデックス0はプログラム名ですので、インデックス1から始めます)。 +もしパースに成功したら、パースしたオブジェクトをマッチで取り出しますし、失敗したならパニックさせます。 +ここまでたどり着いたら、ヘルプフラグが指定されたか調べて、もしそうなら使用法のメッセージを表示します。 +ヘルプメッセージのオプションはGetoptsにより生成済みですので、使用法のメッセージを表示するために追加で必要なのは、プログラム名とテンプレートだけです。 +もしユーザーがヘルプフラグを指定しなかったなら、変数を用意して、対応する引数の値をセットします。 + + +## ロジックを書く + + + + + + +コードを書く順番は人それぞれですが、エラーハンドリングは最後に考えることが多いでしょう。 +これはプログラム全体の設計にとっては、あまり良いことではありません。 +しかし、ラピッドプロトタイピングには便利かもしれません。 +Rustは私たちにエラーハンドリングが明示的であることを( `unwrap` を呼ばせることで)強制しますので、プログラムのどこがエラーを起こすかは、簡単にわかります。 + + + + +このケーススタディでは、ロジックは非常にシンプルです。 +やることは、与えられたCSVデータをパースして、マッチした行にあるフィールドを表示するだけです。 +やってみましょう。 +(ファイルの先頭に `extern crate csv;` を追加することを忘れずに。) ```rust,ignore -// This struct represents the data in each row of the CSV file. -// Type based decoding absolves us of a lot of the nitty gritty error -// handling, like parsing strings as integers or floats. +# // This struct represents the data in each row of the CSV file. +# // Type based decoding absolves us of a lot of the nitty gritty error +# // handling, like parsing strings as integers or floats. +// この構造体はCSVファイルの各行のデータを表現します。 +// 型に基づいたデコードにより、文字列を整数や浮動小数点数にパースして +// しまうといった、核心部分のエラーハンドリングの大半から解放されます。 #[derive(Debug, RustcDecodable)] struct Row { country: String, @@ -2185,9 +2238,12 @@ struct Row { accent_city: String, region: String, - // Not every row has data for the population, latitude or longitude! - // So we express them as `Option` types, which admits the possibility of - // absence. The CSV parser will fill in the correct value for us. +# // Not every row has data for the population, latitude or longitude! +# // So we express them as `Option` types, which admits the possibility of +# // absence. The CSV parser will fill in the correct value for us. + // 人口、経度、緯度などのデータは全ての行にあるわけではありません! + // そこで、これらは不在の可能性を許す `Option` 型で表現します。 + // CSVパーサーは、これらを正しい値で埋めてくれます。 population: Option, latitude: Option, longitude: Option, @@ -2233,60 +2289,95 @@ fn main() { } ``` -Let's outline the errors. We can start with the obvious: the three places that -`unwrap` is called: - -1. [`fs::File::open`](../std/fs/struct.File.html#method.open) - can return an - [`io::Error`](../std/io/struct.Error.html). -2. [`csv::Reader::decode`](http://burntsushi.net/rustdoc/csv/struct.Reader.html#method.decode) - decodes one record at a time, and - [decoding a - record](http://burntsushi.net/rustdoc/csv/struct.DecodedRecords.html) - (look at the `Item` associated type on the `Iterator` impl) - can produce a - [`csv::Error`](http://burntsushi.net/rustdoc/csv/enum.Error.html). -3. If `row.population` is `None`, then calling `expect` will panic. - -Are there any others? What if we can't find a matching city? Tools like `grep` -will return an error code, so we probably should too. So we have logic errors -specific to our problem, IO errors and CSV parsing errors. We're going to -explore two different ways to approach handling these errors. - -I'd like to start with `Box`. Later, we'll see how defining our own -error type can be useful. - -## Error handling with `Box` - -`Box` is nice because it *just works*. You don't need to define your own -error types and you don't need any `From` implementations. The downside is that -since `Box` is a trait object, it *erases the type*, which means the -compiler can no longer reason about its underlying type. - -[Previously](#the-limits-of-combinators) we started refactoring our code by -changing the type of our function from `T` to `Result`. In -this case, `OurErrorType` is just `Box`. But what's `T`? And can we add -a return type to `main`? - -The answer to the second question is no, we can't. That means we'll need to -write a new function. But what is `T`? The simplest thing we can do is to -return a list of matching `Row` values as a `Vec`. (Better code would -return an iterator, but that is left as an exercise to the reader.) - -Let's refactor our code into its own function, but keep the calls to `unwrap`. -Note that we opt to handle the possibility of a missing population count by -simply ignoring that row. +> 訳注: +> +> population count:人口のカウント + + + +ここで、エラーの概要を把握しましょう。 +まずは明白なところ、つまり `unwrap` が呼ばれている3ヶ所から始めます: + + + + + + + + + + + + +1. [`fs::File::open`](../std/fs/struct.File.html#method.open) が [`io::Error`](../std/io/struct.Error.html) を返すかもしれない。 +2. [`csv::Reader::decode`](http://burntsushi.net/rustdoc/csv/struct.Reader.html#method.decode) は1度に1件のレコードをデコードし、 [レコードのデコード](http://burntsushi.net/rustdoc/csv/struct.DecodedRecords.html) (`Iterator` の impl の `Item` 関連型を見てください)は [`csv::Error`](http://burntsushi.net/rustdoc/csv/enum.Error.html) を起こすかもしれない。 +3. もし `row.population` が `None` なら、 `expect` の呼び出しはパニックする。 + + + + + +他にもありますか? +もし一致する都市が見つからなかったら? +`grep` のようなツールはエラーコードを返しますので、ここでも、そうするべきかもしれません。 +つまり、IOエラーとCSVパースエラーの他に、このプログラム特有のエラーロジックがあるわけです。 +これらのエラーを扱うために、2つのアプローチを試してみましょう。 + + + +まず `Box` から始めたいと思います。 +その後で、独自のエラー型を定義すると、どのように便利になるかを見てみましょう。 + + +## `Box` によるエラー処理 + + + + + +`Box` の良いところは *とにかく動く* ことです。 +エラー型を定義して `From` を実装する、といったことは必要ありません。 +これの欠点は `Box` がトレイトオブジェクトなので *型消去* され、コンパイラが背後の型を推測できなくなることです。 + + + + + +[以前](#コンビネータの限界) コードのリファクタリングを、関数の型を `T` から `Result` に変更することから始めました。 +ここでは `私たちのエラー型` は単に `Box` です。 +でも `T` は何になるでしょう? +それに `main` にリターン型を付けられるのでしょうか? + + + + + +2つ目の質問の答えはノーです。できません。 +つまり新しい関数を書くことになります。 +では `T` は何になるでしょう? +一番簡単にできるのは、マッチした `Row` 値のリストを `Vec` として返すことです。 +(もっと良いコードはイテレータを返すかもしれませんが、これは読者の皆さんへの練習問題とします。) + + + + +該当のコードを、専用の関数へとリファクタリングしましょう。 +ただし `unwrap` の呼び出しはそのままにします。 +また、人口のカウントがない場合は、いまは単にその行を無視することに注意してください。 ```rust,ignore struct Row { - // unchanged +# // unchanged + // 変更なし } struct PopulationCount { city: String, country: String, - // This is no longer an `Option` because values of this type are only - // constructed if they have a population count. +# // This is no longer an `Option` because values of this type are only +# // constructed if they have a population count. + // これは `Option` から変更します。なぜなら、この型の値は + // 人口のカウントがあるときだけ構築されるようになったからです。 count: u64, } @@ -2301,7 +2392,8 @@ fn search>(file_path: P, city: &str) -> Vec { for row in rdr.decode::() { let row = row.unwrap(); match row.population { - None => { } // skip it +# // None => { } // skip it + None => { } // スキップする Some(count) => if row.city == city { found.push(PopulationCount { city: row.city, @@ -2340,18 +2432,24 @@ fn main() { ``` -While we got rid of one use of `expect` (which is a nicer variant of `unwrap`), -we still should handle the absence of any search results. + + +`expect` (`unwrap` の少し良い版)の使用を1つ取り除くことができましたが、検索の結果が無いときのハンドリングは、依然として必要です。 -To convert this to proper error handling, we need to do the following: + +このエラーを適切に処理するためには、以下のようにします: -1. Change the return type of `search` to be `Result, - Box>`. -2. Use the [`try!` macro](#code-try-def) so that errors are returned to the - caller instead of panicking the program. -3. Handle the error in `main`. + + + + + +1. `search` のリターン型を `Result, Box>` に変更する。 +2. [`try!` マクロ](#code-try-def) を使用することで、プログラムをパニックする代わりに、エラーを呼び出し元に返す。 +3. `main` でエラーをハンドリングする。 -Let's try it: + +やってみましょう: ```rust,ignore fn search> @@ -2363,7 +2461,8 @@ fn search> for row in rdr.decode::() { let row = try!(row); match row.population { - None => { } // skip it +# // None => { } // skip it + None => { } // スキップする Some(count) => if row.city == city { found.push(PopulationCount { city: row.city, @@ -2381,28 +2480,44 @@ fn search> } ``` -Instead of `x.unwrap()`, we now have `try!(x)`. Since our function returns a -`Result`, the `try!` macro will return early from the function if an -error occurs. - -There is one big gotcha in this code: we used `Box` -instead of `Box`. We did this so we could convert a plain string to an -error type. We need these extra bounds so that we can use the -[corresponding `From` -impls](../std/convert/trait.From.html): +> 訳注: +> +> No matching cities with a population were found.:
+> 条件に合う人口データ付きの街は見つかりませんでした。 + + + + +`x.unwrap()` の代わりに、今では `try!(x)` があります。 +私たちの関数が `Result` を返すので、エラーの発生時、 `try!` マクロは関数の途中で戻ります。 + + + + + + +このコードで1点注意があります: +`Box` の代わりに `Box` を使いました。 +こうすると、プレーンな文字列をエラー型に変換できます。 +[この `From` 実装](../std/convert/trait.From.html) を使うために、このような追加の制限が必要でした。 ```rust,ignore -// We are making use of this impl in the code above, since we call `From::from` -// on a `&'static str`. +# // We are making use of this impl in the code above, since we call `From::from` +# // on a `&'static str`. +// 上のコードでは `&'static str` に対して `From::from` を呼ぶことで、 +// こちらの実装を使おうとしています。 impl<'a, 'b> From<&'b str> for Box -// But this is also useful when you need to allocate a new string for an -// error message, usually with `format!`. +# // But this is also useful when you need to allocate a new string for an +# // error message, usually with `format!`. +// もし `format!` などを使ってエラーメッセージのために新しい文字列を +// 割り当てる場合は、こちらの実装も使えます。 impl From for Box ``` -Since `search` now returns a `Result`, `main` should use case analysis -when calling `search`: + + +`search` が `Result` を返すようになったため、 `main` は `search` を呼ぶときに場合分けをしなければなりません: ```rust,ignore ... @@ -2417,34 +2532,47 @@ match search(&data_file, &city) { ... ``` -Now that we've seen how to do proper error handling with `Box`, let's -try a different approach with our own custom error type. But first, let's take -a quick break from error handling and add support for reading from `stdin`. - -## Reading from stdin - -In our program, we accept a single file for input and do one pass over the -data. This means we probably should be able to accept input on stdin. But maybe -we like the current format too—so let's have both! - -Adding support for stdin is actually quite easy. There are only three things we -have to do: - -1. Tweak the program arguments so that a single parameter—the - city—can be accepted while the population data is read from stdin. -2. Modify the program so that an option `-f` can take the file, if it - is not passed into stdin. -3. Modify the `search` function to take an *optional* file path. When `None`, - it should know to read from stdin. - -First, here's the new usage: + + + +`Box` を使った適切なエラーハンドリングについて見ましたので、次は独自のエラー型による別のアプローチを試してみましょう。 +でもその前に、少しの間、エラーハンドリングから離れて、 `stdin` からの読み込みをサポートしましょう。 + + +## 標準入力から読み込む + + + + +このプログラムでは、入力としてファイルをただ一つ受け取り、1回のパスでデータを処理しています。 +これは、標準入力からの入力を受け付けたほうがいいことを意味しているのかもしれません。 +でも、いまの方法も捨てがたいので、両方できるようにしましょう! + + + +標準入力のサポートを追加するのは実に簡単です。 +やることは3つだけです: + + + + + + + +1. プログラムの引数を微修正して、唯一のパラメータとして「都市」を受け付け、人口データは標準入力から読み込むようにする。 +2. プログラムを修正して、ファイルが標準入力に流し込まれなかったときに、`-f` オプションからファイルを得られるようにする。 +3. `search` 関数を修正して、ファイルパスを `オプションで` 受け取れるようにする。もし `None` なら標準入力から読み込む。 + + +まず、使用法を変更します: ```rust,ignore fn print_usage(program: &str, opts: Options) { println!("{}", opts.usage(&format!("Usage: {} [options] ", program))); } ``` -The next part is going to be only a little harder: + +次のパートはやや難しくなります: ```rust,ignore ... @@ -2468,22 +2596,37 @@ for pop in search(&data_file, &city) { ... ``` -In this piece of code, we take `file` (which has the type -`Option`), and convert it to a type that `search` can use, in -this case, `&Option>`. To do this, we take a reference of -file, and map `Path::new` onto it. In this case, `as_ref()` converts -the `Option` into an `Option<&str>`, and from there, we can -execute `Path::new` to the content of the optional, and return the -optional of the new value. Once we have that, it is a simple matter of -getting the `city` argument and executing `search`. - -Modifying `search` is slightly trickier. The `csv` crate can build a -parser out of -[any type that implements `io::Read`](http://burntsushi.net/rustdoc/csv/struct.Reader.html#method.from_reader). -But how can we use the same code over both types? There's actually a -couple ways we could go about this. One way is to write `search` such -that it is generic on some type parameter `R` that satisfies -`io::Read`. Another way is to just use trait objects: +> 訳注: +> +> Choose an input file, instead of using STDIN:
+> STDINを使う代わりに、入力ファイルを選択する。 + + + + + + + + + +このコードでは(`Option` 型の) `file` を受け取り、 `search` が使える型、つまり今回は `&Option>` へ変換します。 +そのためには `file` の参照を得て、それに対して `Path::new` をマップします。 +このケースでは `as_ref()` が `Option` を `Option<&str>` へ変換しますので、続いて、そのオプション値の中身に対して `Path::new` を実行することで、新しいオプション値を返します。 +ここまでできれば、残りは単に `city` 引数を取得して `search` を実行するだけです。 + + + + + + + + +`search` の修正は少し厄介です。 +`csv` トレイトは [`io::Read` を実装している型](http://burntsushi.net/rustdoc/csv/struct.Reader.html#method.from_reader) からなら、いずれかを問わず、パーサーを構築できます。 +しかし両方の型に同じコードが使えるのでしょうか? +これを実際する方法は2つあります。 +ひとつの方法は `search` を `io::Read` を満たす型パラメータ `R` に対するジェネリックとして書くことです。 +もうひとつの方法は、以下のように、トレイトオブジェクトを使うことです: ```rust,ignore fn search> @@ -2495,19 +2638,24 @@ fn search> Some(ref file_path) => Box::new(try!(fs::File::open(file_path))), }; let mut rdr = csv::Reader::from_reader(input); - // The rest remains unchanged! +# // The rest remains unchanged! + // これ以降は変更なし! } ``` -## Error handling with a custom type + +## 独自のエラー型によるエラー処理 -Previously, we learned how to -[compose errors using a custom error type](#composing-custom-error-types). -We did this by defining our error type as an `enum` and implementing `Error` -and `From`. + + + + +以前、どうやって [独自のエラー型を使ってエラーを合成する](#composing-custom-error-types) のか学びました。 +そのときはエラー型を `enum` 型として定義して、`Error` と `From` を実装することで実現しました。 -Since we have three distinct errors (IO, CSV parsing and not found), let's -define an `enum` with three variants: + + +3つの異なるエラー(IO、CSVのパース、検索結果なし)がありますので `enum` として3つのヴァリアントを定義しましょう: ```rust,ignore #[derive(Debug)] @@ -2518,7 +2666,8 @@ enum CliError { } ``` -And now for impls on `Display` and `Error`: + +`Display` と `Error` を実装します: ```rust,ignore impl fmt::Display for CliError { @@ -2543,11 +2692,15 @@ impl Error for CliError { } ``` -Before we can use our `CliError` type in our `search` function, we need to -provide a couple `From` impls. How do we know which impls to provide? Well, -we'll need to convert from both `io::Error` and `csv::Error` to `CliError`. -Those are the only external errors, so we'll only need two `From` impls for -now: + + + + + +`CliError` を `search` 関数の型に使う前に、いくつかの `From` 実装を用意しなければなりません。 +どのエラーについて用意したらいいのでしょう? +ええと `io::Error` と `csv::Error` の両方を `CliError` に変換する必要があります。 +外部エラーはこれだけですので、今は2つの `From` 実装だけが必要になるのです: ```rust,ignore impl From for CliError { @@ -2563,14 +2716,19 @@ impl From for CliError { } ``` -The `From` impls are important because of how -[`try!` is defined](#code-try-def). In particular, if an error occurs, -`From::from` is called on the error, which in this case, will convert it to our -own error type `CliError`. + + + + +[`try!` がこのように定義](#code-try-def) されているので、 `From` の実装が重要になります。 +特にエラーが起こると、エラーに対して `From::from` が呼ばれますので、このケースでは、それらのエラーが私たち独自のエラー型 `CliError` へ変換されます。 -With the `From` impls done, we only need to make two small tweaks to our -`search` function: the return type and the “not found” error. Here it is in -full: + + + +`From` の実装ができましたので、`search` 関数に2つの小さな修正が必要です: +リターン型と「not found」エラーです。 +全体はこうなります: ```rust,ignore fn search> @@ -2585,7 +2743,8 @@ fn search> for row in rdr.decode::() { let row = try!(row); match row.population { - None => { } // skip it +# // None => { } // skip it + None => { } // スキップする Some(count) => if row.city == city { found.push(PopulationCount { city: row.city, @@ -2603,33 +2762,53 @@ fn search> } ``` -No other changes are necessary. - -## Adding functionality - -Writing generic code is great, because generalizing stuff is cool, and -it can then be useful later. But sometimes, the juice isn't worth the -squeeze. Look at what we just did in the previous step: - -1. Defined a new error type. -2. Added impls for `Error`, `Display` and two for `From`. - -The big downside here is that our program didn't improve a whole lot. -There is quite a bit of overhead to representing errors with `enum`s, -especially in short programs like this. - -*One* useful aspect of using a custom error type like we've done here is that -the `main` function can now choose to handle errors differently. Previously, -with `Box`, it didn't have much of a choice: just print the message. -We're still doing that here, but what if we wanted to, say, add a `--quiet` -flag? The `--quiet` flag should silence any verbose output. - -Right now, if the program doesn't find a match, it will output a message saying -so. This can be a little clumsy, especially if you intend for the program to -be used in shell scripts. - -So let's start by adding the flags. Like before, we need to tweak the usage -string and add a flag to the Option variable. Once we've done that, Getopts does the rest: + +これ以外の変更は不要です。 + + +## 機能を追加する + + + + +汎用的なコードを書くのは素晴らしいことです。 +なぜなら、物事を汎用的にするのはクールですし、後になって役立つかもしれません。 +でも時には、その苦労の甲斐がないこともあります。 +最後のステップで何をしたか振り返ってみましょう: + + + +1. 新しいエラー型を定義した。 +2. `Error` と `Display` の実装を追加し、2つのエラーに対して `From` も実装した。 + + + + +ここでの大きな問題は、このプログラムは全体で見ると大して良くならなかったことです。 +`enum` でエラーを表現するには、多くの付随する作業が必要です。 +特にこのような短いプログラムでは、それが顕著に現れました。 + + + + + + +ここでしたような独自のエラー型を使うのが便利といえる *一つの* 要素は、 `main` 関数がエラーによってどう対処するのかを選択できるようになったことです。 +以前の `Box` では、メッセージを表示する以外、選択の余地はほとんどありませんでした。 +いまでもそうですが、例えば、もし `--quiet` フラグを追加したくなったらどうでしょうか? +`--quiet` フラグは詳細な出力を抑止すべきです。 + + + + +いま現在は、プログラムがマッチするものを見つけられなかったとき、それを告げるメッセージを表示します。 +これは、特にプログラムをシェルスクリプトから使いたいときなどは、扱いにくいかもしれません。 + + + +フラグを追加してみましょう。 +以前したように、使用法についての文字列を少し修正して、オプション変数にフラグを追加します。 +そこまですれば、残りはGetoptsがやってくれます: ```rust,ignore ... @@ -2640,8 +2819,10 @@ opts.optflag("q", "quiet", "Silences errors and warnings."); ... ``` -Now we just need to implement our “quiet” functionality. This requires us to -tweak the case analysis in `main`: + + +後は「quiet」機能を実装するだけです。 +`main` 関数の場合分けを少し修正します: ```rust,ignore match search(&args.arg_data_path, &args.arg_city) { @@ -2653,61 +2834,92 @@ match search(&args.arg_data_path, &args.arg_city) { } ``` -Certainly, we don't want to be quiet if there was an IO error or if the data -failed to parse. Therefore, we use case analysis to check if the error type is -`NotFound` *and* if `--quiet` has been enabled. If the search failed, we still -quit with an exit code (following `grep`'s convention). - -If we had stuck with `Box`, then it would be pretty tricky to implement -the `--quiet` functionality. - -This pretty much sums up our case study. From here, you should be ready to go -out into the world and write your own programs and libraries with proper error -handling. - -# The Short Story - -Since this chapter is long, it is useful to have a quick summary for error -handling in Rust. These are some good “rules of thumb." They are emphatically -*not* commandments. There are probably good reasons to break every one of these -heuristics! - -* If you're writing short example code that would be overburdened by error - handling, it's probably just fine to use `unwrap` (whether that's - [`Result::unwrap`](../std/result/enum.Result.html#method.unwrap), - [`Option::unwrap`](../std/option/enum.Option.html#method.unwrap) - or preferably - [`Option::expect`](../std/option/enum.Option.html#method.expect)). - Consumers of your code should know to use proper error handling. (If they - don't, send them here!) -* If you're writing a quick 'n' dirty program, don't feel ashamed if you use - `unwrap`. Be warned: if it winds up in someone else's hands, don't be - surprised if they are agitated by poor error messages! -* If you're writing a quick 'n' dirty program and feel ashamed about panicking - anyway, then use either a `String` or a `Box` for your - error type (the `Box` type is because of the - [available `From` impls](../std/convert/trait.From.html)). -* Otherwise, in a program, define your own error types with appropriate - [`From`](../std/convert/trait.From.html) - and - [`Error`](../std/error/trait.Error.html) - impls to make the [`try!`](../std/macro.try!.html) - macro more ergonomic. -* If you're writing a library and your code can produce errors, define your own - error type and implement the - [`std::error::Error`](../std/error/trait.Error.html) - trait. Where appropriate, implement - [`From`](../std/convert/trait.From.html) to make both - your library code and the caller's code easier to write. (Because of Rust's - coherence rules, callers will not be able to impl `From` on your error type, - so your library should do it.) -* Learn the combinators defined on - [`Option`](../std/option/enum.Option.html) - and - [`Result`](../std/result/enum.Result.html). - Using them exclusively can be a bit tiring at times, but I've personally - found a healthy mix of `try!` and combinators to be quite appealing. - `and_then`, `map` and `unwrap_or` are my favorites. +> 訳注: +> +> Silences errors and warnings.:エラーや警告を抑止します。 + + + + + +もちろん、IOエラーが起こったり、データのパースに失敗したときは、エラーを抑止したくはありません。 +そこで場合分けを行い、エラータイプが `NotFound` *かつ* `--quiet` が指定されたかを検査しています。 +もし検索に失敗したら、今まで通り( `grep` の動作にならい)なにも表示せず、exitコードと共に終了します。 + + + +もし `Box` で留まっていたら、 `--quiet` 機能を実装するのは、かなり面倒だったでしょう。 + + + + +これが、このケーススタディの締めくくりとなります。 +これからは外の世界に飛び出して、あなた自身のプログラムやライブラリを、適切なエラーハンドリングと共に書くことができるでしょう。 + + +# まとめ + + + + + +この章は長いので、Rustにおけるエラー処理について簡単にまとめたほうがいいでしょう。 +そこには「大まかな法則」が存在しますが、これらは命令的なものでは断固として *ありません* 。 +それぞれのヒューリスティックを破るだけの十分な理由もあり得ます! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +* もし短いサンプルコードを書いていて、エラーハンドリングが重荷になるようなら、 `unwrap` を使っても大丈夫かもしれません( [`Result::unwrap`](../std/result/enum.Result.html#method.unwrap), [`Option::unwrap`](../std/option/enum.Option.html#method.unwrap), [`Option::expect`](../std/option/enum.Option.html#method.expect) のいずれかが使えます)。 + あなたのコードを参考にする人は、正しいエラーハンドリングについて知っているべきです。(そうでなければ、この章を紹介してください!) +* もし即興のプログラムを書いているなら `unwrap` を使うことに罪悪感を持たなくてもいいでしょう。 + ただし警告があります:もしそれが最終的に他の人たちの手に渡るなら、彼らが貧弱なエラーメッセージに動揺してもおかしくありません。 + + + + +* もし即興のプログラムを書いていて、パニックすることに、どうしても後ろめたさを感じるなら、エラー型として `String` か `Box` のいずれかを使ってください( `Box` は [`From` 実装がある](../std/convert/trait.From.html) ので使えます)。 +* これらに該当しないなら、独自のエラー型を定義し、適切な [`From`](../std/convert/trait.From.html) と [`Error`](../std/error/trait.Error.html) を実装することで [`try!`](../std/macro.try!.html) マクロをエルゴノミックにしましょう。 +* もしライブラリを書いていて、そのコードがエラーを起こす可能性があるなら、独自のエラー型を定義し、 [`std::error::Error`](../std/error/trait.Error.html) トレイトを実装してください。 + もし必要なら [`From`](../std/convert/trait.From.html) を実装することで、ライブラリ自身と呼び出し元のコードを書きやすくしてください。 + (Rustの調和性規則(coherence rule) により、呼び出し側では、あなたのエラー型に対して `From` を実装することはできません。 + ライブラリでするべきです。) +* [`Option`](../std/option/enum.Option.html) と [`Result`](../std/result/enum.Result.html) で定義されているコンビネータについて学んでください。 + それだけを使うのは大変ですが、 `try!` と コンビネータを適度にミックスすることは、個人的には、とても魅力的な方法だと考えています。 + `and_then`, `map`, `unwrap_or` が私のお気に入りです。 [1]: ../book/patterns.html [2]: ../std/option/enum.Option.html#method.map