diff --git a/1.6/ja/book/error-handling.md b/1.6/ja/book/error-handling.md index 57389bdc..b2c84116 100644 --- a/1.6/ja/book/error-handling.md +++ b/1.6/ja/book/error-handling.md @@ -1,71 +1,122 @@ -% Error Handling - -Like most programming languages, Rust encourages the programmer to handle -errors in a particular way. Generally speaking, error handling is divided into -two broad categories: exceptions and return values. Rust opts for return -values. - -In this chapter, we intend to provide a comprehensive treatment of how to deal -with errors in Rust. More than that, we will attempt to introduce error handling -one piece at a time so that you'll come away with a solid working knowledge of -how everything fits together. - -When done naïvely, error handling in Rust can be verbose and annoying. This -chapter will explore those stumbling blocks and demonstrate how to use the -standard library to make error handling concise and ergonomic. - -# Table of Contents - -This chapter is very long, mostly because we start at the very beginning with -sum types and combinators, and try to motivate the way Rust does error handling -incrementally. As such, programmers with experience in other expressive type -systems may want to jump around. - -* [The Basics](#the-basics) - * [Unwrapping explained](#unwrapping-explained) - * [The `Option` type](#the-option-type) - * [Composing `Option` values](#composing-optiont-values) - * [The `Result` type](#the-result-type) - * [Parsing integers](#parsing-integers) - * [The `Result` type alias idiom](#the-result-type-alias-idiom) - * [A brief interlude: unwrapping isn't evil](#a-brief-interlude-unwrapping-isnt-evil) -* [Working with multiple error types](#working-with-multiple-error-types) - * [Composing `Option` and `Result`](#composing-option-and-result) - * [The limits of combinators](#the-limits-of-combinators) - * [Early returns](#early-returns) - * [The `try!` macro](#the-try-macro) - * [Defining your own error type](#defining-your-own-error-type) -* [Standard library traits used for error handling](#standard-library-traits-used-for-error-handling) - * [The `Error` trait](#the-error-trait) - * [The `From` trait](#the-from-trait) - * [The real `try!` macro](#the-real-try-macro) - * [Composing custom error types](#composing-custom-error-types) - * [Advice for library writers](#advice-for-library-writers) -* [Case study: A program to read population data](#case-study-a-program-to-read-population-data) - * [Initial setup](#initial-setup) - * [Argument parsing](#argument-parsing) - * [Writing the logic](#writing-the-logic) - * [Error handling with `Box`](#error-handling-with-boxerror) - * [Reading from stdin](#reading-from-stdin) - * [Error handling with a custom type](#error-handling-with-a-custom-type) - * [Adding functionality](#adding-functionality) -* [The short story](#the-short-story) - -# The Basics - -You can think of error handling as using *case analysis* to determine whether -a computation was successful or not. As you will see, the key to ergonomic error -handling is reducing the amount of explicit case analysis the programmer has to -do while keeping code composable. - -Keeping code composable is important, because without that requirement, we -could [`panic`](../std/macro.panic!.html) whenever we -come across something unexpected. (`panic` causes the current task to unwind, -and in most cases, the entire program aborts.) Here's an example: +% エラーハンドリング + + + + + + +他のほとんどのプログラミング言語と同様、Rustはプログラマに、ある決まった作法でエラーを扱うことを促します。 +一般的にエラーハンドリングは、例外、もしくは、戻り値を使ったものの、大きく2つに分類されます。 +Rustでは戻り値を使います。 + + + + + +この章では、Rustでのエラーハンドリングに関わる包括的な扱い方を提示しようと思います。 +単にそれだけではなく、エラーハンドリングのやり方を、ひとつひとつ、順番に積み上げていきます。 +こうすることで、全体がどう組み合わさっているのかの理解が進み、より実用的な知識が身につくでしょう。 + + + + +もし素朴なやり方を用いたなら、Rustにおけるエラーハンドリングは、冗長で面倒なものになり得ます。 +この章では、エラーを処理する上でどのような課題があるかを吟味し、標準ライブラリを使うと、それがいかにシンプルでエルゴノミック(人間にとって扱いやすいもの)に変わるのかを紹介します。 + + +# 目次 + + + + + +この章はとても長くなります。 +というのは、直和型(sum type) とコンビネータから始めることで、Restにおけるエラーハンドリングを徐々に改善していくための動機を与えるからです。 +このような構成ですので、もしすでに他の表現力豊かな型システムの経験があるプログラマでしたら、あちこち拾い読みしたくなるかもしれません。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +* [基礎](#基礎) + * [アンラップ(unwrap) とは](#アンラップunwrap-とは) + * [`Option` 型](#option-型) + * [`Option` 値を合成する](#optiont-値を合成する) + * [`Result` 型](#result-型) + * [整数をパースする](#整数をパースする) + * [`Result` 型エイリアスを用いたイディオム](#result-型エイリアスを用いたイディオム) + * [小休止:アンラップは悪ではない](#小休止アンラップは悪ではない) +* [複数のエラー型を扱う](#working-with-multiple-error-types) + * [`Option` と `Result` を合成する](#composing-option-and-result) + * [コンビネータの限界](#the-limits-of-combinators) + * [早期のリターン](#early-returns) + * [`try!` マクロ](#the-try-macro) + * [独自のエラー型を定義する](#defining-your-own-error-type) +* [標準ライブラリのトレイトによるエラー処理](#standard-library-traits-used-for-error-handling) + * [`Error` トレイト](#the-error-trait) + * [`From` トレイト](#the-from-trait) + * [本当の `try!` マクロ](#the-real-try-macro) + * [独自のエラー型を合成する](#composing-custom-error-types) + * [ライブラリ作者たちへのアドバイス](#advice-for-library-writers) +* [ケーススタディ:人口データを読み込むプログラム](#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) + + +# 基礎 + + + + + +エラーハンドリングとは、ある処理が成功したかどうかを *ケース分析* に基づいて判断するものだと考えられます。 +これから見ていくように、エラーハンドリングをエルゴノミックにするために重要なのは、プログラマがコードを合成可能(composable) に保ったまま、明示的なケース分析の回数を、いかに減らしていくかということです。 + + + + + +コードを合成可能に保つのは重要です。 +なぜなら、もしこの要求がなかったら、想定外のことが起こる度に [`panic`](../std/macro.panic!.html) することを選ぶかもしれないからです。 +(`panic` は現タスクを巻き戻し(unwind) して、ほとんどの場合、プログラム全体をアボートします。) ```rust,should_panic -// Guess a number between 1 and 10. -// If it matches the number we had in mind, return true. Else, return false. +# // Guess a number between 1 and 10. +# // If it matches the number we had in mind, return true. Else, return false. +// 1から10までの数字を予想します。 +// もし予想した数字に一致したらtrueを返し、そうでなけれは、falseを返します。 fn guess(n: i32) -> bool { if n < 1 || n > 10 { panic!("Invalid number: {}", n); @@ -78,14 +129,25 @@ fn main() { } ``` -If you try running this code, the program will crash with a message like this: +> 訳注:文言の意味は +> +> * Invalid number: {}:無効な数字です: {} +> +> ですが、エディタの設定などによっては、ソースコード中の +> コメント以外の場所に日本語を使うとコンパイルできないことがあるので、 +> 英文のままにしてあります。 + + +このコードを実行すると、プログラムがクラッシュして、以下のようなメッセージが表示されます。 ```text thread '
' panicked at 'Invalid number: 11', src/bin/panic-simple.rs:5 ``` -Here's another example that is slightly less contrived. A program that accepts -an integer as an argument, doubles it and prints it. + + +次は、もう少し自然な例です。 +このプログラムは引数として整数を受け取り、2倍した後に表示します。 @@ -94,37 +156,52 @@ use std::env; fn main() { let mut argv = env::args(); - let arg: String = argv.nth(1).unwrap(); // error 1 - let n: i32 = arg.parse().unwrap(); // error 2 + let arg: String = argv.nth(1).unwrap(); // エラー1 + let n: i32 = arg.parse().unwrap(); // エラー2 println!("{}", 2 * n); } ``` -If you give this program zero arguments (error 1) or if the first argument -isn't an integer (error 2), the program will panic just like in the first -example. - -You can think of this style of error handling as similar to a bull running -through a china shop. The bull will get to where it wants to go, but it will -trample everything in the process. - -## Unwrapping explained - -In the previous example, we claimed -that the program would simply panic if it reached one of the two error -conditions, yet, the program does not include an explicit call to `panic` like -the first example. This is because the -panic is embedded in the calls to `unwrap`. - -To “unwrap” something in Rust is to say, “Give me the result of the -computation, and if there was an error, just panic and stop the program.” -It would be better if we just showed the code for unwrapping because it is so -simple, but to do that, we will first need to explore the `Option` and `Result` -types. Both of these types have a method called `unwrap` defined on them. - -### The `Option` type - -The `Option` type is [defined in the standard library][5]: + + + +もし、このプログラムに引数を与えなかったら(エラー1)、あるいは、最初の引数が整数でなかったら(エラー2)、このプログラムは、最初の例と同じようにパニックするでしょう。 + + + + +このようなスタイルのエラーハンドリングは、まるで、陶器店の中を駆け抜ける雄牛のようなものです。 +雄牛は自分の行きたいところへたどり着くでしょう。 +でも彼は、途中にある、あらゆるものを蹴散らしてしまいます。 + + +## アンラップ(unwrap) とは + + + + + + +先ほどの例で、プログラムが2つのエラー条件のいずれかを満たした時に、パニックすると言いました。 +でもこのプログラムは、最初の例とは違って明示的に `panic` を呼び出してはいません。 +実はパニックは `unwrap` の呼び出しの中に埋め込まれているのです。 + + + + + + +Rustでなにかを「アンラップする」時、こう言っているのと同じです。 +「計算結果を取り出しなさい。もしエラーになっていたのなら、パニックを起こしてプログラムを終了させなさい。」 +アンラップのコードはとてもシンプルなので、多分、それを見せたほうが早いでしょう。 +でもそのためには、まず `Option` と `Result` 型について調べる必要があります。 +どちらの型にも `unwrap` という名前のメソッドが定義されています。 + + +### `Option` 型 + + +`Option` 型は [標準ライブラリで定義されています][5]: ```rust enum Option { @@ -133,17 +210,24 @@ enum Option { } ``` -The `Option` type is a way to use Rust's type system to express the -*possibility of absence*. Encoding the possibility of absence into the type -system is an important concept because it will cause the compiler to force the -programmer to handle that absence. Let's take a look at an example that tries -to find a character in a string: + + + + + +`Option` 型は、Rustの型システムを使って *不在の可能性* を示すためのものです。 +不在の可能性を型システムにエンコードすることは、重要なコンセプトです。 +なぜなら、その不在に対処することを、コンパイラがプログラマに強制させるからです。 +では、文字列から文字を検索する例を見てみましょう。 ```rust -// Searches `haystack` for the Unicode character `needle`. If one is found, the -// byte offset of the character is returned. Otherwise, `None` is returned. +# // Searches `haystack` for the Unicode character `needle`. If one is found, the +# // byte offset of the character is returned. Otherwise, `None` is returned. +// `haystack`(干し草の山)からUnicode文字 `needle`(縫い針)を検索します。 +// もし見つかったら、文字のバイトオフセットを返します。見つからなければ、`None` を +// 返します。 fn find(haystack: &str, needle: char) -> Option { for (offset, c) in haystack.char_indices() { if c == needle { @@ -154,16 +238,26 @@ fn find(haystack: &str, needle: char) -> Option { } ``` -Notice that when this function finds a matching character, it doesn't just -return the `offset`. Instead, it returns `Some(offset)`. `Some` is a variant or -a *value constructor* for the `Option` type. You can think of it as a function -with the type `fn(value: T) -> Option`. Correspondingly, `None` is also a -value constructor, except it has no arguments. You can think of `None` as a -function with the type `fn() -> Option`. - -This might seem like much ado about nothing, but this is only half of the -story. The other half is *using* the `find` function we've written. Let's try -to use it to find the extension in a file name. + + + + + + +この関数がマッチする文字を見つけた時、単に `offset` を返すだけではないことに注目してください。 +その代わりに `Some(offset)` を返します。 +`Some` は `Option` 型の *値コンストラクタ* の一つです。 +これは `fn(value: T) -> Option` という型の関数だと考えることもできます。 +これに対応して `None` もまた値コンストラクタですが、こちらには引数がありません。 +`None` は `fn() -> Option` という型の関数だと考えることもできます。 + + + + +何もないことを表すのに、ずいぶん大げさだと感じるかもしれません。 +でもこれはまだ、話の半分に過ぎません。 +残りの半分は、いま書いた `find` 関数を *使う* 場面です。 +これを使って、ファイル名から拡張子を見つけてみましょう。 ```rust # fn find(_: &str, _: char) -> Option { None } @@ -176,15 +270,27 @@ fn main() { } ``` -This code uses [pattern matching][1] to do *case -analysis* on the `Option` returned by the `find` function. In fact, case -analysis is the only way to get at the value stored inside an `Option`. This -means that you, as the programmer, must handle the case when an `Option` is -`None` instead of `Some(t)`. +> 訳注: +> +> * No file extension found:ファイル拡張子は見つかりませんでした +> * File extension: {}:ファイル拡張子:{} -But wait, what about `unwrap`,which we used [`previously`](#code-unwrap-double)? -There was no case analysis there! Instead, the case analysis was put inside the -`unwrap` method for you. You could define it yourself if you want: + + + + + +このコードは `find` 関数が返した `Option` の *ケース分析* に、 [パターンマッチ][1] を使っています。 +実のところ、ケース分析が、`Option` に格納された値を取り出すための唯一の方法なのです。 +これは、`Option` が `Some(t)` ではなく `None` だった時、プログラマであるあなたが、このケースに対処しなければならないことを意味します。 + + + + +でも、ちょっと待ってください。 [さっき](#code-unwrap-double) 使った `unwrap` はどうだったでしょうか? +ケース分析はどこにもありませんでした! +実はケース分析は `unwrap` メソッドの中に埋め込まれていたのです。 +もし望むなら、このように自分で定義することもできます: @@ -205,28 +311,45 @@ impl Option { } ``` -The `unwrap` method *abstracts away the case analysis*. This is precisely the thing -that makes `unwrap` ergonomic to use. Unfortunately, that `panic!` means that -`unwrap` is not composable: it is the bull in the china shop. +> 訳注: +> +> called `Option::unwrap()` on a `None` value:
+> `None` な値に対して `Option:unwpal()` が呼ばれました + + + + +`unwrap` メソッドは *ケース分析を抽象化します* 。このことは確かに `unwrap` をエルゴノミックにしています。 +しかし残念なことに、そこにある `panic!` が意味するものは、`unwrap` が合成可能ではない、つまり、陶器店の中の雄牛だということです。 -### Composing `Option` values + +### `Option` 値を合成する -In an [example from before](#code-option-ex-string-find), -we saw how to use `find` to discover the extension in a file name. Of course, -not all file names have a `.` in them, so it's possible that the file name has -no extension. This *possibility of absence* is encoded into the types using -`Option`. In other words, the compiler will force us to address the -possibility that an extension does not exist. In our case, we just print out a -message saying as such. + + + + + + + +[先ほどの例](#code-option-ex-string-find) では、ファイル名から拡張子を見つけるために `find` をどのように使うかを見ました。 +当然ながら全てのファイル名に `.` があるわけではなく、拡張子のないファイル名もあり得ます。 +このような *不在の可能性* は `Option` を使うことによって、型の中にエンコードされています。 +すなわち、コンパイラは、拡張子が存在しない可能性に対処することを、私たちに強制してくるわけです。 +今回は単に、そうなったことを告げるメッセージを表示するようにしました。 -Getting the extension of a file name is a pretty common operation, so it makes -sense to put it into a function: + + +ファイル名から拡張子を取り出すことは一般的な操作ですので、それを関数にすることは理にかなっています。 ```rust # fn find(_: &str, _: char) -> Option { None } -// Returns the extension of the given file name, where the extension is defined -// as all characters proceeding the first `.`. -// If `file_name` has no `.`, then `None` is returned. +# // Returns the extension of the given file name, where the extension is defined +# // as all characters proceeding the first `.`. +# // If `file_name` has no `.`, then `None` is returned. +// 与えられたファイル名の拡張子を返す。拡張子の定義は、最初の +// `.` に続く、全ての文字である。 +// もし `file_name` に `.` がなければ、`None` が返される。 fn extension_explicit(file_name: &str) -> Option<&str> { match find(file_name, '.') { None => None, @@ -235,23 +358,32 @@ fn extension_explicit(file_name: &str) -> Option<&str> { } ``` -(Pro-tip: don't use this code. Use the + + + +(プロ向けのヒント:このコードは使わず、代わりに標準ライブラリの [`extension`](../std/path/struct.Path.html#method.extension) -method in the standard library instead.) - -The code stays simple, but the important thing to notice is that the type of -`find` forces us to consider the possibility of absence. This is a good thing -because it means the compiler won't let us accidentally forget about the case -where a file name doesn't have an extension. On the other hand, doing explicit -case analysis like we've done in `extension_explicit` every time can get a bit -tiresome. - -In fact, the case analysis in `extension_explicit` follows a very common -pattern: *map* a function on to the value inside of an `Option`, unless the -option is `None`, in which case, just return `None`. - -Rust has parametric polymorphism, so it is very easy to define a combinator -that abstracts this pattern: +メソッドを使ってください) + + + + + + + +このコードはいたってシンプルですが、ひとつだけ注目して欲しいのは、`find` の型が不在の可能性について考慮することを強制していることです。 +これは良いことです。なぜなら、コンパイラが私たちに、ファイル名が拡張子を持たないケースを、うっかり忘れないようにしてくれるからです。 +しかし一方で、 `extension_explicit` でしたような明示的なケース分析を毎回続けるのは、なかなか面倒です。 + + + + +実は `extension_explicit` でのケース分析は、ごく一般的なパターンである、`Option` への *map* の適用に当てはめられます。 +これは、もしオプションが `None` なら `None` を返し、そうでなけれは、オプションの中の値に関数を適用する、というパターンです。 + + + +Rustはパラメトリック多相をサポートしていますので、このパターンを抽象化するためのコンビネータが簡単に定義できます: @@ -264,26 +396,35 @@ fn map(option: Option, f: F) -> Option where F: FnOnce(T) -> A { } ``` -Indeed, `map` is [defined as a method][2] on `Option` in the standard library. + +もちろん `map` は、標準のライブラリの `Option` で [メソッドとして定義されています][2]。 -Armed with our new combinator, we can rewrite our `extension_explicit` method -to get rid of the case analysis: + + +新しいコンビネータを手に入れましたので、 `extension_explicit` メソッドを書き直して、ケース分析を省きましょう: ```rust # fn find(_: &str, _: char) -> Option { None } -// Returns the extension of the given file name, where the extension is defined -// as all characters proceeding the first `.`. -// If `file_name` has no `.`, then `None` is returned. +# // Returns the extension of the given file name, where the extension is defined +# // as all characters proceeding the first `.`. +# // If `file_name` has no `.`, then `None` is returned. +// 与えられたファイル名の拡張子を返す。拡張子の定義は、最初の +// `.` に続く、全ての文字である。 +// もし `file_name` に `.` がなければ、`None` が返される。 fn extension(file_name: &str) -> Option<&str> { find(file_name, '.').map(|i| &file_name[i+1..]) } ``` -One other pattern we commonly find is assigning a default value to the case -when an `Option` value is `None`. For example, maybe your program assumes that -the extension of a file is `rs` even if none is present. As you might imagine, -the case analysis for this is not specific to file extensions - it can work -with any `Option`: + + + + + +もう一つの共通のパターンは、`Option` の値が `None` だった時のデフォルト値を与えることです。 +例えばファイルの拡張子がない時は、それを `rs` とみなすようなプログラムを書きたくなるかもしれません。 +ご想像の通り、このようなケース分析はファイルの拡張子に特有のものではありません。 +どんな `Option` でも使えるでしょう: ```rust fn unwrap_or(option: Option, default: T) -> T { @@ -294,8 +435,10 @@ fn unwrap_or(option: Option, default: T) -> T { } ``` -The trick here is that the default value must have the same type as the value -that might be inside the `Option`. Using it is dead simple in our case: + + +ここでの仕掛けは、`Option` に入れる値と同じ型になるよう、デフォルト値の型を制限していることです。 +これを使うのは、すごく簡単です: ```rust # fn find(haystack: &str, needle: char) -> Option { @@ -316,21 +459,30 @@ fn main() { } ``` -(Note that `unwrap_or` is [defined as a method][3] on `Option` in the -standard library, so we use that here instead of the free-standing function we -defined above. Don't forget to check out the more general [`unwrap_or_else`][4] -method.) + + + + +(`unwrap_or` は、標準のライブラリの `Option` で、 [メソッドとして定義されています][3] ので、いま定義したフリースタンディングな関数の代わりに、そちらを使いましょう。) -There is one more combinator that we think is worth paying special attention to: -`and_then`. It makes it easy to compose distinct computations that admit the -*possibility of absence*. For example, much of the code in this section is -about finding an extension given a file name. In order to do this, you first -need the file name which is typically extracted from a file *path*. While most -file paths have a file name, not *all* of them do. For example, `.`, `..` or -`/`. + + + + + + + +もうひとつ特筆すべきコンビネータがあります。それは `and_then` です。これを使うと *不在の可能性* を考慮しながら、別々の処理を簡単に組み合わせることができます。 +例えば、この節のほとんどのコードは、与えられたファイル名について拡張子を見つけだします。 +そのためには、まずファイル *パス* から取り出したファイル名が必要です。 +大抵のパスにはファイル名がありますが、 *全て* がというわけではありません。 +例えば `.`, `..`, `/` などは例外です。 + + + +つまり、与えられたファイル *パス* から拡張子を見つけ出せるか、トライしなければなりません。 +まず明示的なケース分析から始めましょう: -So, we are tasked with the challenge of finding an extension given a file -*path*. Let's start with explicit case analysis: ```rust # fn extension(file_name: &str) -> Option<&str> { None } @@ -345,17 +497,23 @@ fn file_path_ext_explicit(file_path: &str) -> Option<&str> { } fn file_name(file_path: &str) -> Option<&str> { - // implementation elided +# // implementation elided + // 実装は省略 unimplemented!() } ``` -You might think that we could just use the `map` combinator to reduce the case -analysis, but its type doesn't quite fit. Namely, `map` takes a function that -does something only with the inner value. The result of that function is then -*always* [rewrapped with `Some`](#code-option-map). Instead, we need something -like `map`, but which allows the caller to return another `Option`. Its generic -implementation is even simpler than `map`: + + + + + + +ケース分析を減らすために単に `map` コンビネータを使えばいいと思うかもしれませんが、型にうまく適合しません。 +なぜなら `map` が引数にとる関数は、中の値だけに適用されるからです。 +そして関数が返した値は *必ず* [`Some` でラップされ直します](#code-option-map) 。 +つまりこの代わりに、 `map` に似ていながら、呼び出し元が別の `Option` を返せるしくみが必要です。 +これの汎用的な実装は `map` よりもシンプルです: ```rust fn and_then(option: Option, f: F) -> Option @@ -367,7 +525,8 @@ fn and_then(option: Option, f: F) -> Option } ``` -Now we can rewrite our `file_path_ext` function without explicit case analysis: + +では、明示的なケース分析を省くように、 `file_path_ext` を書き直しましょう: ```rust # fn extension(file_name: &str) -> Option<&str> { None } @@ -377,22 +536,32 @@ fn file_path_ext(file_path: &str) -> Option<&str> { } ``` -The `Option` type has many other combinators [defined in the standard -library][5]. It is a good idea to skim this list and familiarize -yourself with what's available—they can often reduce case analysis -for you. Familiarizing yourself with these combinators will pay -dividends because many of them are also defined (with similar -semantics) for `Result`, which we will talk about next. + + + + + +`Option` 型には、他にもたくさんのコンビネータが [標準ライブラリで定義されています][5] 。 +それらの一覧をざっと眺めて、なにがあるか知っておくといいでしょう。 +大抵の場合、ケース分析を減らすのに役立ちます。 +それらのコンビネータに慣れるための努力は、すぐに報われるでしょう。 +なぜなら、そのほとんどは次に話す `Result` 型でも、(よく似たセマンティクスで)定義されているからです。 -Combinators make using types like `Option` ergonomic because they reduce -explicit case analysis. They are also composable because they permit the caller -to handle the possibility of absence in their own way. Methods like `unwrap` -remove choices because they will panic if `Option` is `None`. + + + + +コンビネータは明示的なケース分析を減らしてくれるので、 `Option` のような型をエルゴノミックにします。 +またこれらは *不在の可能性* を、呼び出し元がそれに合った方法で扱えるようにするので、合成可能だといえます。 +`unwrap` のようなメソッドは、 `Option` が `None` の時にパニックを起こすので、このような選択の機会を与えません。 -## The `Result` type + +## `Result` 型 -The `Result` type is also -[defined in the standard library][6]: + + +`Result` 型も [標準ライブラリで定義されています][6] 。 @@ -403,29 +572,42 @@ enum Result { } ``` -The `Result` type is a richer version of `Option`. Instead of expressing the -possibility of *absence* like `Option` does, `Result` expresses the possibility -of *error*. Usually, the *error* is used to explain why the execution of some -computation failed. This is a strictly more general form of `Option`. Consider -the following type alias, which is semantically equivalent to the real -`Option` in every way: + + + + + + +`Result` 型は `Option` 型の豪華版です。 +`Option` のように *不在* の可能性を示す代わりに、`Result` は *エラー* になる可能性を示します。 +通常 *エラー* は、なぜ処理が実行に失敗したのかを説明するために用いられます。 +これは厳密には `Option` をさらに一般化した形式だといえます。 +以下のような型エイリアスがあるとしましょう。 +これは全てにおいて、本物の `Option` と等しいセマンティクスを持ちます。 ```rust type Option = Result; ``` -This fixes the second type parameter of `Result` to always be `()` (pronounced -“unit” or “empty tuple”). Exactly one value inhabits the `()` type: `()`. (Yup, -the type and value level terms have the same notation!) + + + +これは `Result` の2番目の型パラメータを `()` (「ユニット」または「空タプル」と発音します)に固定したものです。 +`()` 型のただ一つの値は `()` です。 +(そうなんです。型レベルと値レベルの項が、全く同じ表記法を持ちます!) -The `Result` type is a way of representing one of two possible outcomes in a -computation. By convention, one outcome is meant to be expected or “`Ok`” while -the other outcome is meant to be unexpected or “`Err`”. + + + +`Result` 型は、処理の結果がとりうる2つの可能性のうち、1つを表すための方法です。 +慣例に従い、一方が期待されている結果、つまり「`Ok`」となり、もう一方が予想外の結果、つまり「`Err`」になります。 -Just like `Option`, the `Result` type also has an -[`unwrap` method -defined][7] -in the standard library. Let's define it: + + + + +`Option` と全く同じように、`Result` 型も標準ライブラリで [`unwrap` メソッドが定義されています][7] 。 +定義してみましょう: ```rust # enum Result { Ok(T), Err(E) } @@ -440,22 +622,35 @@ impl Result { } ``` -This is effectively the same as our [definition for -`Option::unwrap`](#code-option-def-unwrap), except it includes the -error value in the `panic!` message. This makes debugging easier, but -it also requires us to add a [`Debug`][8] constraint on the `E` type -parameter (which represents our error type). Since the vast majority -of types should satisfy the `Debug` constraint, this tends to work out -in practice. (`Debug` on a type simply means that there's a reasonable -way to print a human readable description of values with that type.) +> 訳注: +> +> called `Result::unwrap()` on an `Err` value: {:?}":
+> `Err` 値 {:?} に対して `Result::unwrap()` が呼ばれました + + + + + + + + + +これは実質的には私たちの [`Option::unwrap` の定義](#code-option-def-unwrap) と同じですが、 `panic!` メッセージにエラーの値が含まれているところが異なります。 +これはデバッグをより簡単にしますが、一方で、(エラーの型を表す)型パラメータ `E` に [`Debug`][8] 制約を付けることが求められます。 +大半の型は `Debug` 制約を満たしているので、実際のところ、うまくいく傾向にあります。 +(`Debug` が型に付くということは、単にその型の値が、人間が読める形式で表示できることを意味しています。) -OK, let's move on to an example. + +では、例を見ていきましょう。 -### Parsing integers + +### 整数をパースする -The Rust standard library makes converting strings to integers dead simple. -It's so easy in fact, that it is very tempting to write something like the -following: + + + +Rustの標準ライブラリを使うと、文字列を整数に変換することが、すごく簡単にできます。 +あまりにも簡単なので、実際のところ、以下のように書きたいという誘惑に負けることがあります: ```rust fn double_number(number_str: &str) -> i32 { @@ -468,19 +663,27 @@ fn main() { } ``` -At this point, you should be skeptical of calling `unwrap`. For example, if -the string doesn't parse as a number, you'll get a panic: + + +すでにあなたは、`unwrap` を呼ぶことについて懐疑的になっているはずです。 +例えば、文字列が数字としてパースできなければ、パニックが起こります。 ```text thread '
' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }', /home/rustbuild/src/rust-buildbot/slave/beta-dist-rustc-linux/build/src/libcore/result.rs:729 ``` -This is rather unsightly, and if this happened inside a library you're -using, you might be understandably annoyed. Instead, we should try to -handle the error in our function and let the caller decide what to -do. This means changing the return type of `double_number`. But to -what? Well, that requires looking at the signature of the [`parse` -method][9] in the standard library: + + + + + + +これは少し目障りです。 +もしあなたが使っているライブラリの中でこれが起こされたら、イライラするに違いありません。 +代わりに、私たちの関数の中でエラーを処理し、呼び出し元にどうするのかを決めさせるべきです。 +そのためには、`double_number` の戻り値の型(リターン型)を変更しなければなりません。 +でも、一体何に? +ええと、これはつまり、標準ライブラリの [`parse` メソッド][9] のシグネチャを見ろということです。 ```rust,ignore impl str { @@ -488,27 +691,45 @@ impl str { } ``` -Hmm. So we at least know that we need to use a `Result`. Certainly, it's -possible that this could have returned an `Option`. After all, a string either -parses as a number or it doesn't, right? That's certainly a reasonable way to -go, but the implementation internally distinguishes *why* the string didn't -parse as an integer. (Whether it's an empty string, an invalid digit, too big -or too small.) Therefore, using a `Result` makes sense because we want to -provide more information than simply “absence.” We want to say *why* the -parsing failed. You should try to emulate this line of reasoning when faced -with a choice between `Option` and `Result`. If you can provide detailed error -information, then you probably should. (We'll see more on this later.) - -OK, but how do we write our return type? The `parse` method as defined -above is generic over all the different number types defined in the -standard library. We could (and probably should) also make our -function generic, but let's favor explicitness for the moment. We only -care about `i32`, so we need to [find its implementation of -`FromStr`](../std/primitive.i32.html) (do a `CTRL-F` in your browser -for “FromStr”) and look at its [associated type][10] `Err`. We did -this so we can find the concrete error type. In this case, it's -[`std::num::ParseIntError`](../std/num/struct.ParseIntError.html). -Finally, we can rewrite our function: + + + + + + + + + + +うむ。最低でも `Result` を使わないといけないことはわかりました。 +もちろん、これが `Option` を戻すようにすることも可能だったでしょう。 +結局のところ、文字列が数字としてパースできたかどうかが知りたいわけですよね? +それも悪いやり方ではありませんが、実装の内側では *なぜ* 文字列が整数としてパースできなかったを、ちゃんと区別しています。 +(空の文字列だったのか、有効な数字でなかったのか、大きすぎたり、小さすぎたりしたのか。) +従って、`Result` を使ってより多くの情報を提供するほうが、単に「不在」を示すことよりも理にかなっています。 +今後、もし `Option` と `Result` のどちらを選ぶという事態に遭遇した時は、このような理由付けのやり方を真似てみてください。 +もし詳細なエラー情報を提供できるのなら、多分、それをしたほうがいいでしょう。 +(後ほど別の例もお見せます。) + + + + + + + + + + + +それでは、リターン型をどう書きましょうか? +上の `parse` メソッドは一般化されているので、標準ライブラリにある、あらゆる数値型について定義されています。 +この関数を同じように一般化することもできますが(そして、そうするべきでしょうが)、今は明快さを優先しましょう。 +`i32` だけを扱うことにしますので、それの [`FromStr` の実装がどうなっているか探しましょう](../std/primitive.i32.html) 。 +(ブラウザで `CTRL-F` を押して「FromStr」を探します。) +そして [関連型(associated type)][10] から `Err` を見つけます。 +こうすれば、具体的なエラー型が見つかります。 +この場合、それは [`std::num::ParseIntError`](../std/num/struct.ParseIntError.html) です。 +これでようやく関数を書き直せます: ```rust use std::num::ParseIntError; @@ -528,12 +749,18 @@ fn main() { } ``` -This is a little better, but now we've written a lot more code! The case -analysis has once again bitten us. + + +これで少し良くなりましたが、たくさんのコードを書いてしまいました! +ケース分析に、またしてもやられたわけです。 -Combinators to the rescue! Just like `Option`, `Result` has lots of combinators -defined as methods. There is a large intersection of common combinators between -`Result` and `Option`. In particular, `map` is part of that intersection: + + + +コンビネータに助けを求めましょう! +ちょうど `Option` と同じように `Result` にもたくさんのコンビネータが、メソッドとして定義されています。 +`Result` と `Option` の間では、共通のコンビネータが数多く存在します。 +例えば `map` も共通なものの一つです: ```rust use std::num::ParseIntError; @@ -550,24 +777,34 @@ fn main() { } ``` -The usual suspects are all there for `Result`, including -[`unwrap_or`](../std/result/enum.Result.html#method.unwrap_or) and -[`and_then`](../std/result/enum.Result.html#method.and_then). -Additionally, since `Result` has a second type parameter, there are -combinators that affect only the error type, such as -[`map_err`](../std/result/enum.Result.html#method.map_err) (instead of -`map`) and [`or_else`](../std/result/enum.Result.html#method.or_else) -(instead of `and_then`). - -### The `Result` type alias idiom - -In the standard library, you may frequently see types like -`Result`. But wait, [we defined `Result`](#code-result-def) to -have two type parameters. How can we get away with only specifying -one? The key is to define a `Result` type alias that *fixes* one of -the type parameters to a particular type. Usually the fixed type is -the error type. For example, our previous example parsing integers -could be rewritten like this: + + + + + + + + +`Result` でいつも候補にあがるのは [`unwrap_or`](../std/result/enum.Result.html#method.unwrap_or) と [`and_then`](../std/result/enum.Result.html#method.and_then) です。 +さらに `Result` は2つ目の型パラメータを取りますので、エラー型だけに影響を与える [`map_err`](../std/result/enum.Result.html#method.map_err) (`map` に相当)と [`or_else`](../std/result/enum.Result.html#method.or_else) (`and_then` に相当)もあります。 + + +### `Result` 型エイリアスを用いたイディオム + + + + + + + + +標準ライブラリでは `Result` のような型をよく見ると思います。 +でも、待ってください。 +2つの型パラメータを取るように [`Result` を定義したはずです](#code-result-def) 。 +どうして、1つだけを指定して済んだのでしょう? +種を明かすと、`Result` の型エイリアスを定義して、一方の型パラメータを特定の型に *固定* したのです。 +通常はエラー型の方を固定します。 +例えば、先ほどの整数のパースの例は、こう書き換えることもできます。 ```rust use std::num::ParseIntError; @@ -580,54 +817,86 @@ fn double_number(number_str: &str) -> Result { } ``` -Why would we do this? Well, if we have a lot of functions that could return -`ParseIntError`, then it's much more convenient to define an alias that always -uses `ParseIntError` so that we don't have to write it out all the time. - -The most prominent place this idiom is used in the standard library is -with [`io::Result`](../std/io/type.Result.html). Typically, one writes -`io::Result`, which makes it clear that you're using the `io` -module's type alias instead of the plain definition from -`std::result`. (This idiom is also used for -[`fmt::Result`](../std/fmt/type.Result.html).) - -## A brief interlude: unwrapping isn't evil - -If you've been following along, you might have noticed that I've taken a pretty -hard line against calling methods like `unwrap` that could `panic` and abort -your program. *Generally speaking*, this is good advice. - -However, `unwrap` can still be used judiciously. What exactly justifies use of -`unwrap` is somewhat of a grey area and reasonable people can disagree. I'll -summarize some of my *opinions* on the matter. - -* **In examples and quick 'n' dirty code.** Sometimes you're writing examples - or a quick program, and error handling simply isn't important. Beating the - convenience of `unwrap` can be hard in such scenarios, so it is very - appealing. -* **When panicking indicates a bug in the program.** When the invariants of - your code should prevent a certain case from happening (like, say, popping - from an empty stack), then panicking can be permissible. This is because it - exposes a bug in your program. This can be explicit, like from an `assert!` - failing, or it could be because your index into an array was out of bounds. - -This is probably not an exhaustive list. Moreover, when using an -`Option`, it is often better to use its -[`expect`](../std/option/enum.Option.html#method.expect) -method. `expect` does exactly the same thing as `unwrap`, except it -prints a message you give to `expect`. This makes the resulting panic -a bit nicer to deal with, since it will show your message instead of -“called unwrap on a `None` value.” - -My advice boils down to this: use good judgment. There's a reason why the words -“never do X” or “Y is considered harmful” don't appear in my writing. There are -trade offs to all things, and it is up to you as the programmer to determine -what is acceptable for your use cases. My goal is only to help you evaluate -trade offs as accurately as possible. - -Now that we've covered the basics of error handling in Rust, and -explained unwrapping, let's start exploring more of the standard -library. + + + +なぜ、こうするのでしょうか? +もし `ParseIntError` を返す関数をたくさん定義するとしたら、常に `ParseIntError` を使うエイリアスを定義したほうが便利だからです。 +こうすれば、同じことを何度も書かずに済みます。 + + + + + + + +標準ライブラリで、このイディオムが際立って多く使われている場所では、[`io::Result`](../std/io/type.Result.html) を用いています。 +それらは通常 `io::Result` のように書かれ、`std::result` のプレーンな定義の代わりに `io` モジュールの型エイリアスを使っていることが、明確にわかるようになっています。 + + +## 小休止:アンラップは悪ではない + + + + +これまでの説明を読んだあなたは、 `unwrap` のような `panic` を起こし、プログラムをアボートするようなメソッドについて、私がきっぱりと否定する方針をとっていたことに気づいたかもしれません。 +*一般的には* これは良いアドバイスです。 + + + + +しかしながら `unwrap` を使うのが懸命なこともあります。 +どんな場合が `unwrap` の使用を正当化できるのかについては、グレーな部分があり、人によって意見が分かれます。 +ここで、この問題についての、私の *個人的な意見* をまとめたいと思います。 + + + + + +* **即興で書いたサンプルコード。** + サンプルコードや簡単なプログラムを書いていて、エラーハンドリングが単に重要でないこともあります。 + このような時に `unwrap` の便利さは、とても魅力的に映るでしょう。 + これに打ち勝つのは難しいことです。 + + + + + + +* **パニックがプログラムのバグの兆候となる時。** + コードの中の不変条件が、ある特定のケースの発生を未然に防ぐ時(例えば、空のスタックから取り出そうとしたなど)、パニックを起こしても差し支えありません。 + なぜなら、そうすることでプログラムに潜むバグが明るみに出るからです。 + これは `assert!` の失敗のような明示的な要因によるものだったり、配列のインデックスが境界から外れたからだったりします。 + + + + + + + + +これは多分、完全なリストではないでしょう。 +さらに `Option` を使う時は、ほとんどの場合で [`expect`](../std/option/enum.Option.html#method.expect) メソッドを使う方がいいでしょう。 +`expect` は `unwrap` とほぼ同じことをしますが、 `expect` では与えられたメッセージを表示するところが異なります。 +この方が結果として起こったパニックを、少し扱いやすいものにします。 +なぜなら「 `None` な値に対してアンラップが呼ばれました」というメッセージの代わりに、指定したメッセージが表示されるからです。 + + + + + + +私のアドバイスを突き詰めると、よく見極めなさい、ということです。 +私の書いた文章の中に「決して、Xをしてはならない」とか「Yは有害だと考えよう」といった言葉が現れないのには、れっきとした理由があります。 +あるユースケースでこれが容認できるかどうかは、プログラマであるあなたの判断に委ねられます。 +私が目指していることは、あなたがトレードオフをできるかぎり正確に評価できるよう、手助けをすることなのです。 + + + + +これでRustにおけるエラーハンドリングの基礎をカバーできました。 +また、アンラップについても解説しました。 +では標準ライブラリをもっと探索していきましょう。 # Working with multiple error types diff --git a/TranslationTable.md b/TranslationTable.md index a11b38d1..95d3d326 100644 --- a/TranslationTable.md +++ b/TranslationTable.md @@ -10,6 +10,7 @@ | English | 日本語 |:-------------------------------|:------------- +| abort | アボート | (lockの) acquire | 獲得 | aggregate type | 合成型 | alignment | アラインメント @@ -28,13 +29,16 @@ | borrowing | 借用 | bounds | 境界 | bug | バグ -| channel | チャネル | capture | キャプチャ +| case analysis | ケース分析 +| channel | チャネル | closure | クロージャ | coercion | 型強制 +| combinator | コンビネータ | comma | カンマ | compile-time error | コンパイル時エラー | compiler | コンパイラ +| composable | 合成可能 | computer science | コンピュータサイエンス | concurrency | 並行性 | constant | 定数 @@ -48,8 +52,8 @@ | dereferencing | 参照外し | destructor | デストラクタ | destructuring | 分配 -| directory | ディレクトリ | directive | ディレクティブ +| directory | ディレクトリ | discriminant | 判別子 | distribution | 配布物 | diverge | ダイバージ @@ -57,12 +61,17 @@ | documentation comment | ドキュメンテーションコメント | documentation test | ドキュメンテーションテスト | early return | 早期リターン -| error | エラー +| empty tuple | 空タプル +| encode | エンコード | enum | 列挙型 | equality | 等値性 +| ergonomic | エルゴノミック(人間にとって扱いやすいもの) +| error | エラー +| error handling | エラーハンドリング | expression statement | 式文 | feature | フィーチャ | foreign | 他言語 +| free-standing function | フリースタンディングな関数 | generic parameter | ジェネリックパラメータ | generics | ジェネリクス | growable | 伸張可能 @@ -77,8 +86,8 @@ | installer | インストーラ | interpolate | インターポーレートする | interpolation | インターポーレーション -| keyword | キーワード | Intrinsics | Intrinsic +| keyword | キーワード | Lang Items | Lang Item | leak | リーク | lending | 貸付け @@ -93,8 +102,8 @@ | memory | メモリ | method | メソッド | move | ムーブ -| mutable | ミュータブル | mutability | ミュータビリティ +| mutable | ミュータブル | mutable binding | ミュータブルな束縛 | null | ヌル | offline | オフライン @@ -107,6 +116,7 @@ | ownership | 所有権 | panic | パニック | parametric polymorphism | パラメトリック多相 +| parse | パース、パースする | patch | パッチ | pattern | パターン | performance | パフォーマンス @@ -121,17 +131,21 @@ | release | リリース | (lockの) release | 解放 | return | 返す +| return type | リターン型 +| return value | 戻り値 | safe | 安全 | safety check | 安全性検査 | scope | スコープ | scoped | スコープ化された | script | スクリプト +| semantics | セマンティクス | shadow | 覆い隠す | shadowing | シャドーイング | signature | シグネチャ | signed | 符号付き | slice | スライス | slicing | スライシング +| standard library | 標準ライブラリ | string | 文字列 | string interpolation | 文字列インターポーレーション | struct | 構造体 @@ -141,18 +155,25 @@ | syntactic sugar | 糖衣構文 | system | システム | tagged union | タグ付き共用体 +| term | 項 | thread-locality | スレッドローカル性 | threadsafe | スレッドセーフ | tick | クオート | trait | トレイト -| type inference | 型推論 +| tuple | タプル +| type alias | 型エイリアス | type family | 型族 +| type inference | 型推論 +| type parameter | 型パラメータ | uninstall | アンインストール +| unit 注: `()` の読み | ユニット | Universal Function Call Syntax | 共通の関数呼び出し構文 | unsafe | アンセーフ | unsigned | 符号無し | unsized type | サイズ不定型 | unwinding | 巻き戻し +| unwrap | アンラップ +| value constructor | 値コンストラクタ | variable | 変数 | variable binding | 変数束縛 | variant | ヴァリアント