0Pricing
Learn Rust Coding · レッスン

`Result`による堅牢なエラー処理

`Result`列挙型、`?`演算子、カスタムエラー型を使い、回復力のあるアプリケーションのためのRustらしいエラー処理を実装します。

「`Result`による堅牢なエラー処理」はCoddyKit上の無料Learn Rust Codingレッスンです。 これはレッスン3/3です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLearn Rust Coding学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Learn Rust Codingコースには全3レッスンが含まれています。

Rust でのエラー処理

エラーはプログラミングにつきものです。Rust では、エラーを主に回復可能なエラーと回復不能なエラーに分けます。

  • 回復不能なエラーは通常、バグを示すもので、panic! によってプログラムが停止します。
  • 回復可能なエラーは、ファイルが見つからない場合のように、問題は発生したものの対処できるエラーです。

Rust では、回復可能なエラーを適切に処理できます。

`panic!` と `Result` の比較

回復不能なエラーが発生すると、Rust は panic! マクロを呼び出します。これによりスタックが巻き戻されてプログラムが終了し、通常はエラーメッセージが表示されます。

回復可能なエラーには、Rust の Result 列挙型を使います。この列挙型は、処理が成功したか(Ok)、失敗したか(Err)を示すため、プログラム側で次の処理を判断できます。

`Result` 列挙型

Result 列挙型は、概念的には次のように定義されます。

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

  • T は、成功時に返される値の型を表します。
  • E は、失敗時に返されるエラーの型を表します。

これは、関数が失敗する可能性を明示的に示す強力な方法です。

`Result` のパターンマッチ

Result を処理する最も一般的な方法は、match 式を使うことです。これにより、Result が Ok か Err かに応じて、異なるコードブロックを実行できます。

数値の解析を試みる次の例を実行してみましょう。

fn parse_and_add(text: &str) -> Result<i32, std::num::ParseIntError> {
    text.parse::<i32>()
}

fn main() {
    let num_str = "123";
    let result = parse_and_add(num_str);

    match result {
        Ok(num) => println!("Parsed number: {}", num),
        Err(e) => println!("Error parsing: {}", e),
    }

    let bad_str = "abc";
    let bad_result = parse_and_add(bad_str);

    match bad_result {
        Ok(num) => println!("Parsed number: {}", num),
        Err(e) => println!("Error parsing: {}", e),
    }
}

Result のアンラップ(慎重に)

処理が失敗しないと確信できる場合や、失敗したらプログラムをクラッシュさせたい場合があります。unwrap() や expect() などのメソッドは、Ok の値を取り出します。

  • unwrap(): Ok の値を返します。Err の場合はパニックを起こします。
  • expect("message"): unwrap() と同じですが、カスタムパニックメッセージを指定できます。

これらは、主にテストや、失敗から本当に回復できない場合に限り、慎重に使用してください。

`?` 演算子の導入

すべての Result に対して match 文を書くと、コードが冗長になることがあります。? 演算子を使うと、エラーの伝播を簡潔に記述できます。

Result を返す式の後に ? を置くと、次の2つの処理が行われます。

  • Result が Err の場合、現在の関数からその Err を直ちに返します。
  • Result が Ok の場合、Ok の値をアンラップして実行を続けます。

`?` 演算子の使用例

? 演算子を使うと、失敗する可能性のある関数をすっきり記述できます。ただし、? を使う関数自体が Result(または Option)を返す必要があることを覚えておいてください。

次のファイル読み込みの例で、? によってエラー処理がどのように簡潔になるか確認しましょう。

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

fn read_username_from_file() -> Result<String, io::Error> {
    let mut f = File::open("hello.txt")?; // Propagates error if file not found
    let mut s = String::new();
    f.read_to_string(&mut s)?; // Propagates error if read fails
    Ok(s)
}

fn main() {
    // Create a dummy file for the example to work
    // In a real scenario, this file might not exist
    std::fs::write("hello.txt", "CoddyKit User").unwrap();

    match read_username_from_file() {
        Ok(username) => println!("Username: {}", username),
        Err(e) => println!("Error reading username: {}", e),
    }

    // Clean up the dummy file
    std::fs::remove_file("hello.txt").unwrap();
}

カスタムエラー型

組み込みのエラー型では、十分に具体的でない場合があります。列挙型を使って独自のカスタムエラー型を定義し、アプリケーションで起こり得るさまざまな失敗を表現できます。

これにより、エラーメッセージがわかりやすくなり、呼び出し側で特定のエラーごとに異なる処理を行えるようになります。

よく使われる方法は、関数やモジュールが生成する可能性のあるすべてのエラーを列挙する enum を作成することです。

カスタムエラーで `?` を使う

? 演算子によって低レベルのエラー(io::Error など)をカスタムエラー型に自動変換するには、カスタムエラーの enum に、その低レベルのエラーに対する From トレイトを実装する必要があります。

これにより、Rust にあるエラー型から別のエラー型へ変換する方法を伝えられるため、エラーをスムーズに伝播できます。

use std::io;
use std::fs::File;
use std::io::Read;

#[derive(Debug)]
enum MyError {
    Io(io::Error),
    Parse(std::num::ParseIntError)
}

impl From<io::Error> for MyError {
    fn from(error: io::Error) -> Self {
        MyError::Io(error)
    }
}

impl From<std::num::ParseIntError> for MyError {
    fn from(error: std::num::ParseIntError) -> Self {
        MyError::Parse(error)
    }
}

fn read_config(path: &str) -> Result<i32, MyError> {
    let mut file = File::open(path)?; // io::Error converted to MyError::Io
    let mut contents = String::new();
    file.read_to_string(&mut contents)?; // io::Error converted to MyError::Io
    let value = contents.trim().parse::<i32>()?; // ParseIntError converted to MyError::Parse
    Ok(value)
}

fn main() {
    // Create a dummy file for the example
    std::fs::write("config.txt", "123").unwrap();

    match read_config("config.txt") {
        Ok(val) => println!("Config value: {}", val),
        Err(e) => println!("Failed to read config: {:?}", e),
    }

    std::fs::remove_file("config.txt").unwrap(); // Clean up

    // Example with a non-existent file
    match read_config("non_existent.txt") {
        Ok(val) => println!("Config value: {}", val),
        Err(e) => println!("Failed to read config: {:?}", e),
    }
}

理解度チェック

次の Rust コードについて考えてみましょう。ファイル data.txt が存在しない場合、出力はどうなりますか。

復習: 堅牢なエラー処理

Rust で堅牢なエラー処理を使いこなせるようになりました。

  • Result 列挙型: 成功を表す Ok(T) と、失敗を表す Err(E) を区別します。
  • match: Result の各バリアントを処理する基本的な方法です。
  • ? 演算子: Err の値を呼び出し元へ伝播するための強力な省略記法です。
  • カスタムエラー: 列挙型を使って独自のエラー型を定義し、明確で具体的な処理を実現します。
  • From トレイト: ? を使う際に、低レベルのエラーをカスタムエラー型へスムーズに変換できるようにします。

次は、型システムの高度な機能を見ていきましょう。

よくある質問

「`Result`による堅牢なエラー処理」レッスンは無料ですか?

はい。「`Result`による堅牢なエラー処理」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Learn Rust Codingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Learn Rust Codingコースには全3レッスンが含まれています。

「`Result`による堅牢なエラー処理」で何を学びますか?

`Result`列挙型、`?`演算子、カスタムエラー型を使い、回復力のあるアプリケーションのためのRustらしいエラー処理を実装します。 ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Learn Rust Codingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLearn Rust Codingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/3です。

「`Result`による堅牢なエラー処理」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLearn Rust Codingレッスンでコードを書いて実行できますか?

はい。すべてのLearn Rust Codingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. モジュールによるコード整理
  2. Crateによる依存関係管理
  3. `Result`による堅牢なエラー処理
← Learn Rust Codingに戻る