RustのResult型によるエラー処理をマスターする
Ethan Miller
Product Engineer · Leapcell

RustのResult型によるエラー処理をマスターする
Rustは、独自のエラー処理機構を提供するシステムプログラミング言語です。Rustでは、エラーは回復可能なエラーと回復不可能なエラーの2種類に分類されます。回復可能なエラーに対して、Rustはそれらを処理するためにResult型を提供します。
Result型の定義
Result型は、OkとErrの2つのバリアントを持つ列挙型です。Okバリアントは成功した操作を表し、成功値を持ちます。一方、Errバリアントは失敗した操作を表し、エラー値を持ちます。
以下は、Result型の定義です。
enum Result<T, E> { Ok(T), Err(E), }
ここで、Tは成功値の型を表し、Eはエラー値の型を表します。
Result型の使用
Result型は、関数戻り値としてよく使用されます。関数が正常に実行されると、Okバリアントを返します。失敗すると、Errバリアントを返します。
以下は簡単な例です。
fn divide(numerator: f64, denominator: f64) -> Result<f64, String> { if denominator == 0.0 { Err("Cannot divide by zero".to_string()) } else { Ok(numerator / denominator) } } fn main() { let result = divide(4.0, 2.0); match result { Ok(value) => println!("Result: {}", value), Err(e) => println!("Error: {}", e), } }
この例では、divide関数は分子と分数の2つの引数を取ります。分母が0の場合、Errバリアントを返します。それ以外の場合は、Okバリアントを返します。
main関数では、divide関数を呼び出し、matchステートメントを使用して戻り値を処理します。戻り値がOkの場合、結果が出力されます。Errの場合、エラーメッセージが出力されます。
Resultを使用したエラーの処理方法
Result型を返す関数を呼び出す場合、潜在的なエラーを処理する必要があります。これを行うには、いくつかの方法があります。
matchステートメントの使用
matchステートメントは、RustでResult型のエラーを処理する最も一般的な方法です。これにより、戻り値に基づいて異なる操作を実行できます。
簡単な例を次に示します。
fn divide(numerator: f64, denominator: f64) -> Result<f64, String> { if denominator == 0.0 { Err("Cannot divide by zero".to_string()) } else { Ok(numerator / denominator) } } fn main() { let result = divide(4.0, 2.0); match result { Ok(value) => println!("Result: {}", value), Err(e) => println!("Error: {}", e), } }
この例では、matchステートメントを使用してdivide関数の戻り値を処理します。Okを返す場合、結果が出力されます。Errを返す場合、エラーメッセージが出力されます。
if letステートメントの使用
if letステートメントは、matchの簡略化されたバージョンです。1つのケースのみを照合でき、他のケースを処理する必要はありません。if letステートメントは、Result型の1つのケースのみを気にする場合によく使用されます。
簡単な例を次に示します。
fn divide(numerator: f64, denominator: f64) -> Result<f64, String> { if denominator == 0.0 { Err("Cannot divide by zero".to_string()) } else { Ok(numerator / denominator) } } fn main() { let result = divide(4.0, 2.0); if let Ok(value) = result { println!("Result: {}", value); } }
この例では、if letステートメントを使用してdivide関数の戻り値を処理します。Okを返す場合、結果が出力されます。それ以外の場合、何も起こりません。
?演算子の使用
?演算子は、関数内からエラーを簡単に伝播できるRustの特別な構文です。Result型を返す関数を呼び出す場合、?演算子はエラー処理を簡素化できます。
簡単な例を次に示します。
fn divide(numerator: f64, denominator: f64) -> Result<f64, String> { if denominator == 0.0 { Err("Cannot divide by zero".to_string()) } else { Ok(numerator / denominator) } } fn calculate(numerator: f64, denominator: f64) -> Result<f64, String> { let result = divide(numerator, denominator)?; Ok(result * 2.0) } fn main() { let result = calculate(4.0, 2.0); match result { Ok(value) => println!("Result: {}", value), Err(e) => println!("Error: {}", e), } }
この例では、calculate関数は内部でdivide関数を呼び出し、?演算子を使用してエラー処理を簡素化します。divideがErrを返す場合、calculateはすぐにErrを返します。それ以外の場合、実行は継続されます。
Resultの一般的なメソッド
Result型には、エラー処理をより便利にするいくつかの便利なメソッドが用意されています。
is_okメソッドとis_errメソッド
is_okメソッドとis_errメソッドは、ResultがそれぞれOkバリアントであるかErrバリアントであるかを確認します。
簡単な例を次に示します。
fn divide(numerator: f64, denominator: f64) -> Result<f64, String> { if denominator == 0.0 { Err("Cannot divide by zero".to_string()) } else { Ok(numerator / denominator) } } fn main() { let result = divide(4.0, 2.0); if result.is_ok() { println!("Result: {}", result.unwrap()); } else { println!("Error: {}", result.unwrap_err()); } }
この例では、is_okメソッドを使用して、divideの戻り値がOkであるかどうかを確認します。もしそうなら、unwrapを使用して成功値を取得して出力します。それ以外の場合は、unwrap_errを使用してエラーメッセージを取得して出力します。
unwrapメソッドとunwrap_errメソッド
unwrapメソッドとunwrap_errメソッドは、それぞれResultから成功値またはエラー値を取得します。Resultが予期されるバリアントでない場合、パニックが発生します。
簡単な例を次に示します。
fn divide(numerator: f64, denominator: f64) -> Result<f64, String> { if denominator == 0.0 { Err("Cannot divide by zero".to_string()) } else { Ok(numerator / denominator) } } fn main() { let result = divide(4.0, 2.0); let value = result.unwrap(); println!("Result: {}", value); }
この例では、unwrapを使用してdivide関数の成功値を取得します。戻り値がOkでない場合、パニックが発生します。
expectメソッドとexpect_errメソッド
expectメソッドとexpect_errメソッドは、unwrapメソッドとunwrap_errメソッドに似ていますが、カスタムエラーメッセージを指定できます。Resultが予期されるバリアントでない場合、パニックが発生し、指定されたメッセージが出力されます。
簡単な例を次に示します。
fn divide(numerator: f64, denominator: f64) -> Result<f64, String> { if denominator == 0.0 { Err("Cannot divide by zero".to_string()) } else { Ok(numerator / denominator) } } fn main() { let result = divide(4.0, 2.0); let value = result.expect("Failed to divide"); println!("Result: {}", value); }
この例では、expectを使用してdivide関数の成功値を取得します。戻り値がOkでない場合、パニックが発生し、指定されたエラーメッセージが出力されます。
Resultの機能と利点
Result型には、次の機能と利点があります。
- 明示的なエラー処理:
Result型は、プログラマーにエラーを明示的に処理することを強制し、エラーが無視または見落とされるのを防ぎます。 - 型安全性:
Result型は、任意の型の成功値またはエラー値を保持できるジェネリック型であり、型安全性を確保し、型変換エラーを防ぎます。 - 便利なエラー伝播: Rustは、関数からエラーを簡単に伝播するための
?演算子を提供します。 - 簡単な構成:
Result型は、and、or、and_then、or_elseなどのさまざまな構成メソッドを提供し、複数のResult値を簡単に組み合わせることができます。
実際のコードでのResultの使用
実際のコードでは、カスタムエラー型を定義し、Result型を使用してエラー情報を返すことがよくあります。
簡単な例を次に示します。
use std::num::ParseIntError; type Result<T> = std::result::Result<T, MyError>; #[derive(Debug)] enum MyError { DivideByZero, ParseIntError(ParseIntError), } impl From<ParseIntError> for MyError { fn from(e: ParseIntError) -> Self { MyError::ParseIntError(e) } } fn divide(numerator: &str, denominator: &str) -> Result<f64> { let numerator: f64 = numerator.parse()?; let denominator: f64 = denominator.parse()?; if denominator == 0.0 { Err(MyError::DivideByZero) } else { Ok(numerator / denominator) } } fn main() { let result = divide("4", "2"); match result { Ok(value) => println!("Result: {}", value), Err(e) => println!("Error: {:?}", e), } }
この例では:
DivideByZeroとParseIntErrorの2つのバリアントを含むカスタムエラー型MyErrorを定義します。- 型エイリアス
Resultを定義し、MyErrorをエラー型として設定します。 divide関数は2つの文字列引数を取り、それらをf64に解析しようとします。解析が失敗すると、?演算子はエラーを伝播します。分母が0の場合、Errバリアントが返されます。それ以外の場合、関数はOkを返します。
main関数では、divideを呼び出し、matchステートメントを使用して戻り値を処理します。Okを返す場合、結果を出力します。Errを返す場合、エラーメッセージを出力します。
Resultを使用したファイル読み取り/書き込みエラーの処理
ファイル操作を行う場合、ファイルが見つからない、または権限が不十分など、さまざまなエラーが発生する可能性があります。これらのエラーは、Result型を使用して処理できます。
簡単な例を次に示します。
use std::fs; use std::io; fn read_file(path: &str) -> Result<String, io::Error> { fs::read_to_string(path) } fn main() { let result = read_file("test.txt"); match result { Ok(content) => println!("File content: {}", content), Err(e) => println!("Error: {}", e), } }
この例では:
read_file関数はファイルパスを引数として取り、fs::read_to_stringを使用してファイルの内容を読み取ります。fs::read_to_stringは、ファイルの内容を含む成功値とio::Error型のエラー値を持つResult型を返します。mainでは、read_fileを呼び出し、matchを使用して戻り値を処理します。Okを返す場合、ファイルの内容が出力されます。Errを返す場合、エラーメッセージが出力されます。
Resultを使用したネットワークリクエストエラーの処理
ネットワークリクエストを行う場合、接続タイムアウトやサーバーエラーなど、さまざまなエラーが発生する可能性があります。これらのエラーも、Result型を使用して処理できます。
簡単な例を次に示します。
use std::io; use std::net::TcpStream; fn connect(host: &str) -> Result<TcpStream, io::Error> { TcpStream::connect(host) } fn main() { let result = connect("example.com:80"); match result { Ok(stream) => println!("Connected to {}", stream.peer_addr().unwrap()), Err(e) => println!("Error: {}", e), } }
この例では:
connect関数はホストアドレスを引数として取り、TcpStream::connectを使用してTCP接続を確立します。TcpStream::connectは、TcpStream型の成功値とio::Error型のエラー値を持つResult型を返します。mainでは、connectを呼び出し、matchを使用して戻り値を処理します。Okを返す場合、接続情報が出力されます。Errを返す場合、エラーメッセージが出力されます。
Resultとエラー処理のベストプラクティス
Resultでエラーを処理する場合、次のベストプラクティスは、より良いコードを作成するのに役立ちます。
- カスタムエラー型を定義する: カスタムエラー型は、エラー情報の整理と管理をより効果的に行うのに役立ちます。
?演算子を使用してエラーを伝播する:?演算子は、関数からエラーを簡単に伝播できるようにします。unwrapとexpectの過度の使用を避ける: これらのメソッドは、Errバリアントが発生した場合にパニックを引き起こします。代わりに、matchまたはif letを使用してエラーを適切に処理します。- 構成メソッドを使用して複数の
Result値を組み合わせる:and、or、and_then、or_elseなどのメソッドは、複数のResult値を効率的に組み合わせるのに役立ちます。
Rustプロジェクトのホスティングに最適なLeapcellをご紹介します。
Leapcellは、Webホスティング、非同期タスク、Redis向けの次世代サーバーレスプラットフォームです。
多言語サポート
- Node.js、Python、Go、またはRustで開発できます。
無制限のプロジェクトを無料でデプロイ
- 使用量に対してのみ料金が発生します。リクエストも料金もかかりません。
比類のないコスト効率
- アイドル料金なしの従量課金制。
- 例:$25で、平均応答時間60msで694万リクエストをサポートします。
合理化された開発者エクスペリエンス
- 簡単な設定を実現する直感的なUI。
- 完全に自動化されたCI/CDパイプラインとGitOps統合。
- 実用的な洞察を得るためのリアルタイムメトリクスとロギング。
簡単なスケーラビリティと高性能
- 高い同時実行性を容易に処理するための自動スケーリング。
- 運用オーバーヘッドはゼロ。構築に集中するだけです。
詳細については、ドキュメントをご覧ください。
Xでフォローしてください:@LeapcellHQ



