🦀

RustのResult::expect()の引数は、仮定法過去で考えるとしっくりくる

に公開2

RustのResult<T>型は関数の戻り値の型としてよく使われます。この型のメソッドであるexpect()の引数は、「エラーメッセージ」と解説されることがあります。しかしそれは混乱を招く考え方です。

理解のためにはこの引数を仮定法過去のメッセージと考えた方がしっくりします。

I wish I could open a file. 
→ ファイルを開くことができたらなぁ(現実には開くことができない)。

そもそもライブラリの文書にはexpect()に期待する動作を書けとある

expect()の引数には何を渡すべきでしょうか。こういう時にはドキュメントを参照すべきです。

Rustのドキュメントを参照すると、Result::expectには以下のように書いてあります。

We recommend that expect messages are used to describe the reason you expect the Result should be Ok.

戻り値がOkであるために期待していることを書け、ということです。expectの引数はエラーメッセージではないのです。

このあたりは公式ドキュメントにも誤解があったようです。2025年4月時点のRustプログラミング言語のエラー時にパニックするショートカット: unwrapとexpect(Rust 1.58準拠)には、以下のようなプログラム例が挙げられています。

src/main.rs
use std::fs::File;

fn main() {
    // hello.txtを開くのに失敗しました
    let f = File::open("hello.txt").expect("Failed to open hello.txt");
}

この例では、expect()の引数はエラーメッセージです。「『hello.txtを開くことに失敗した』を期待する」ではどうにも格好が付きません。

これが英語版のThe Rust Programming Language Shortcuts for Panic on Error: unwrap and expect(Rust 1.82.0準拠)では

src/main.rs
use std::fs::File;

fn main() {
    let greeting_file = File::open("hello.txt")
        .expect("hello.txt should be included in this project");
}

となっており、引数には期待する結果が書いてあります。

多分、英語版の方は何らかの指摘があって改定の時に修正したのでしょう。「『hello.txtがプロジェクト内に無ければならない』を期待する」でしたら、まぁまぁ辻褄はあっています。

でも、本当にそうでしょうか。

expect()は戻り値がOkではないときにメッセージを出力する

私がexpectの使用例を読んでもやもやした気持ちになるのは、それが関数の結果がどうであるか宣言しているように思えるからです。もう一度例を見ましょう。

src/main.rs
use std::fs::File;

fn main() {
    let greeting_file = File::open("hello.txt")
        .expect("hello.txt should be included in this project");
}

このような記述だと、expect()の引数には、open()関数がどのような結果をもたらすのかを宣言すべきに思えます。というのは、英語で" open excpect ... "は「openは...であることを期待する」と読めるからです。

Rustは英語ではありません。それはわかっています。Rustのこの形の記述は関数の戻り値に対してメソッドを呼び出すチェーンであって、英文ではありません。しかし"return value expect ... "と考えるにしても、メッセージが失敗時(戻り値がOkではない時)にのみ表示されることから、やはり気持ちの悪さを感じます。

C++の話ですが、Google Testを使う時にEXPECT()マクロを「結果の期待値」のテストに使うため、expect()の引数に環境への期待を書くのも違和感を感じます。

こういった気持ち悪さは、引数を含めてResult::expect()を仮定法過去の文として考えればある程度解消できます。

英語ならexpectではなくwish

正直な話、私はexpect()というメソッド名が悪いと感じます。メソッド名をwish()としておけば、もうすこし飲み込みやすくなっていたでしょう。

学校で以下のような英語の仮定法過去文法を勉強したはずです。

I wish I were a bird.
→ 私が鳥だったらよかったのに(現実には鳥ではない)。

この考え方で例題を書き直してみましょう。

src/main
use std::fs::File;

fn main() {
    let greeting_file = File::open("hello.txt")
        .wish("hello.txt would be included in this project");
}

File::open()を主語として、「hello.txtがプロジェクト内に有ったらなあ(しかし現実には無い)」と読むことができます。これはwish()メソッドが「open()の結果がOkではない時にのみメッセージを出力する」ことを鑑みて、英語としてもまぁまぁしっくりきます。

仮定法過去でメッセージを考え直す

あれこれ書きましたが、文句を言っても仕方ありません。

Resultにはwish()メソッドは無く、有るのはexpect()メソッドです。expectを使う時には頭の中で仮定法過去を念じながら書くと良いでしょう。

なお、RustのドキュメントのResult::expectには、expect()のメッセージには"should"を使うことを念頭に置くと良いとあります。

Discussion

kanaruskanarus

typo: except [ → expect ]
( 一括置換するとよさそう )

堀江誠一堀江誠一

ご指摘ありがとうございます。ほんとにお恥ずかしい。