blog.dopana

Back

Rust基礎シリーズの第10回(最終回)です。Rustでは、エラーを大きく2つのカテゴリに分類します。panic! による「回復不能なエラー」と、Result<T, E> による「回復可能なエラー」です。

panic! による回復不能なエラー#

配列の範囲外アクセスなど、プログラムが継続不可能な致命的状態に遭遇すると、Rustは panic! マクロを実行します。これによりエラーメッセージが出力され、スタックが展開(unwind)されてプロセスが終了します。

fn main() {
    // panic!("クラッシュしました");
    let v = vec![1, 2, 3];
    // v[99]; // 自動的にパニックが発生
}
rust

プログラム自体で回復できない不正な状態が発生した場合にのみ panic! を使用します。

Result による回復可能なエラー#

ファイルのオープンや数値のパースなど、失敗することが予測される操作では、関数は Result<T, E> を返します。

enum Result<T, E> {
    Ok(T),
    Err(E),
}
rust

Resultmatch を使って検査します。

use std::fs::File;

fn main() {
    let greeting_file_result = File::open("hello.txt");

    let _greeting_file = match greeting_file_result {
        Ok(file) => file,
        Err(error) => panic!("ファイルオープン時の問題: {error:?}"),
    };
}
rust

ショートカット: unwrapexpect#

Result には、T を抽出するか Err 時にパニックを発生させる便利メソッドが用意されています。

let file = File::open("hello.txt").unwrap();
let file = File::open("hello.txt").expect("hello.txtはこのディレクトリに存在する必要があります");
rust

本番コードでは、クラッシュログに有用なコンテキスト情報を提供できるため unwrap よりも expect を使用することが推奨されます。

? 演算子によるエラー伝播#

関数を執筆する際、エラーをその場で処理せず呼び出し元に返したい場合が頻繁にあります。? 演算子を使用すると、簡潔にエラーを伝播させることができます。

use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
    let mut username = String::new();
    File::open("hello.txt")?.read_to_string(&mut username)?;
    Ok(username)
}

fn main() -> Result<(), io::Error> {
    let username = read_username_from_file()?;
    println!("ユーザー名: {username}");
    Ok(())
}
rust

? で評価された操作が Err を返した場合、その Err は即座を包括する関数からリターンされます。

まとめ#

Rustのエラー処理は非常に明確です。回復不能な失敗には panic!、回復可能なエラーには Result<T, E>、そして人間工学的な伝播には ? 演算子を使用します。これで全10回のRust基礎シリーズは完結です!

参考資料#