「TAURI」と「Rust」の「Option型」と「Result型」を使いこなそう

2024年2月27日(火)
大西 武 (オオニシ タケシ)
第10回の今回は「TAURI」や「Rust」で値なしを場合分けする「Option型」と、例外処理の代わりをする「Result型」について解説します。

はじめに

今回は、これまで解説が蔑ろになっていた「Option型」と「Result型」を解説します。どちらも上手くいった場合と失敗した場合の場合分けをします。この辺もRustが堅牢に動作する厳密さの1つですね。

Rustでは値なしの「NULL」があり得ないので、Option型で値がある場合とない場合を場合分けします。多くのプログラミング言語は値がないことをNULLや「nil」などで表していましたが、これはプログラムがおかしな挙動になる原因にもなっていたので、RustではOption型が採用されたのでしょう。

また、Rustでは例外処理がない代わりにResult型で例外が起きなかった場合とエラーが起きた場合を場合分けします。多くのプログラミング言語は例外処理に「try」文を使うことが多かったのですが、RustではOption型の書式と一部を共通化したのではと思われます。

RustのOption型とResult型

Rustプロジェクトの作成

まず、TAURIではなくRustのみでプロジェクトを作成します。追加で必要なクレートはありません。適当なフォルダをカレントディレクトリにして、次のコマンドを実行して「option_result」などという名前でプロジェクトを新規作成します。それから作成した「option_result」フォルダをカレントディレクトリにします。

・Rustプロジェクトの作成コマンド
$ cargo new option_result

Option型

Option型は値が正しい場合と間違っている場合によって場合分けしますが、Option型の変数のままでは実際に値を使うことができません。Option型の変数を「match」文で場合分けし、値が正しい場合は「Some」、間違っている場合は「None」になります。「Option型のままでは使えない」と言いましたが、正しい場合は「Some(引数) => { 引数を取り出す。 }」というような使い方をします。

関数やメソッドでのOption型の戻り値の書式は「fn 関数名 -> Option<u64>」で、戻り値のOption型のu64型が値が存在する場合のラップした値の型です。

図1:Option型の図

・Option型の場合分け

次のサンプルコードを「$ cargo run」コマンドで実行したら「positive」関数の引数に10を渡し、正の整数なので「u64」型にキャストしてSome(10)を返します。match文で「p」変数から「Some」のラップを取り除いて10を取得し、"正の整数10です。"をターミナルに表示します。

ここで「let p = positive(-10);」とすると負の整数の引数なので「None」が返り、match文で"負の整数です。"をターミナルに表示します。

・「src」→「main.rs」ファイルのOption型のサンプルコード
//メイン関数
fn main() {
  let p = positive(10);
  match p {
    Some(num) => { println!("正の整数{}です。",num); }
    None => { println!("負の整数です。"); }
  }
}
//正の整数か調べる関数
fn positive(i:i64) -> Option<u64> {
  if i >= 0 {
    Some(i as u64)
  } else {
    None
  }
}

【サンプルコードの解説】
main関数でpositive関数の引数に10を渡しOption型をp変数に代入します。
match文でp変数が存在する場合Someからnumの値を取り出してターミナルに値を表示し、存在しない場合(None)、"負の整数です。"をターミナルに表示します。
positive関数で「i」引数が正の整数の場合i64型のi引数をu64型にキャストしてSomeにラップして返し、負の整数の場合はNoneをラップして返します。

・Option型の場合分けを簡略化

Option型は100%近く値が存在する場合に、毎回match文を使うのは面倒だと思われるでしょう。次のサンプルコードのようにmatch文を使わず「unwrap」メソッドで値が存在した場合の値をすぐに取り出すこともできます。電子レンジで温めた食べ物はラップを外さないと中身が食べられないように、unwrapも文字通りラップを外さないと中身の値が取り出せません。

ただし、unwrapは値が存在しない場合にパニックが起きてプログラムが終了してしまいます。100%近く値が存在する場合だけunwrapを使った方が良いでしょう。

・「src」→「main.rs」ファイルの簡略化したOption型のサンプルコード
//メイン関数
fn main() {
  let p = positive(10).unwrap();
  println!("正の整数{}です。",p);
}
//正の整数か調べる関数
fn positive(i:i64) -> Option<u64> {
  if i >= 0 {
    Some(i as u64)
  } else {
    None
  }
}

【サンプルコードの解説】
main関数でpositive関数の引数に10を渡しており必ず値が存在するためunwrapします。
"正の整数10です。"をターミナルに表示します。

ここで「let p = positive(-10).unwrap();」とすると負の整数の引数なのでNoneが返りパニックになるため、代わりにデフォルト値を使えるようにする「unwrap_or」メソッドを使います。

・「src」→「main.rs」ファイルのパニックを起こさないように簡略化したOption型のサンプルコード
//メイン関数
fn main() {
  let p = positive(-10).unwrap_or(10);
  println!("正の整数にした場合{}です。",p);
}
//正の整数か調べる関数
fn positive(i:i64) -> Option<u64> {
  if i >= 0 {
    Some(i as u64)
  } else {
    None
  }
}

【サンプルコードの解説】
main関数でpositive関数の引数に-10を渡しており値が存在しないため、unwrap_orでデフォルト値10を取得します。
"正の整数にした場合10です。"をターミナルに表示します。

Result型

Result型は値が正しい場合と例外の場合によって場合分けしますが、Result型の変数のままでは実際に値を使うことができません。Result型の変数を「match」文で場合分けし、値が正しい場合は「Ok」、例外の場合は「Err」になります。

「Result型のままでは使えない」と言いましたが、正しい場合は「Ok(引数) => { 引数を取り出す。 }」、エラーの場合は「Err(引数) => { エラー内容などの引数を取り出す。 }」というような使い方をします。

Option型と似ていますが、Result型はエラーの場合も値を引数に渡せます。最初にも言ったように値にNULLがあり得る場合をOption型で実装し、例外処理の場合をResult型で実装します。関数やメソッドでのResult型の戻り値の書式は「fn 関数名 -> Result<u64, String>」で、これは戻り値のResult型のu64型が値が正しい場合のラップした値の型、Stringがエラーの場合の値の型です。ただしエラーの場合の型はエラー用の型が用意されていることが多く、例えばファイル入出力の場合「std::io::Error」などがあります。

図2:Result型の図

・Result型の場合分け

次のサンプルコードを実行したら、positive関数の引数に正の整数10が渡されており値が正しいためOkを返し、match文でResult型のOkからラップを外したnum引数を取得して"正の整数10です。"をターミナルに表示します。

ここで「let p = positive(-10);」とすると負の整数の引数なのでErrが返り、match文で「err」引数をErrからラップを外して"負の整数です。"をターミナルに表示します。

・「src」→「main.rs」ファイルのResult型のサンプルコード
//メイン関数
fn main() {
  let p = positive(10);
  match p {
    Ok(num) => { println!("正の整数{}です。",num); }
    Err(err) => { println!("{}",err); }
  }
}
//正の整数か調べる関数
fn positive(i:i64) -> Result<u64, String> {
  if i >= 0 {
    Ok(i as u64)
  } else {
    Err(String::from("負の整数です。"))
  }
}

【サンプルコードの解説】
main関数でpositive関数の引数に10を渡しResult型をp変数に代入します。
match文でp変数が正しい場合はOkからnumの値を取り出してターミナルに値を表示し、例外の場合はErrからerrの値を取り出して"負の整数です。"をターミナルに表示します。
positive関数でiが正の整数の場合はi64型の「i」引数をu64型にキャストしてOkにラップして返し、負の整数の場合はErrにエラー内容をラップして返します。

・Result型の場合分けの簡略化

例外が起きる可能性がとても低いのに、毎回match文で場合分けするのは面倒に思われるでしょう。その場合は即座にunwrapメソッドでResult型のOkの中身を取り出すことができます。Result型もOption型と同様、unwrapでラップを外して中身の食べ物を使えます。

ただしOption型同様、例外が起きた場合にパニックを起こします。パニックを起こすとプログラムが終了してしまうので、例外が起きない場合だけunwrapを使いましょう。

・「src」→「main.rs」ファイルの簡略化したResult型のサンプルコード
//メイン関数
fn main() {
  let p = positive(10).unwrap();
  println!("正の整数{}です。",p);
}
//正の整数か調べる関数
fn positive(i:i64) -> Result<u64, String> {
  if i >= 0 {
    Ok(i as u64)
  } else {
    Err(String::from("負の整数です。"))
  }
}

【サンプルコードの解説】
main関数でpositive関数の引数に10を渡しており、必ず値が正しいためunwrapします。
"正の整数10です。"をターミナルに表示します。

ここで「let p = positive(-10).unwrap();」としたら負の整数の引数なのでパニックを起こします。それを回避するために「unwrap_or」メソッドを使って例外が起きてErrを返した場合、代わりにunwrap_orの引数をデフォルト値として返します。

・「src」→「main.rs」ファイルのパニックを起こさないように簡略化したResult型のサンプルコード
//メイン関数
fn main() {
  let p = positive(-10).unwrap_or(10);
  println!("正の整数にした場合{}です。",p);
}
//正の整数か調べる関数
fn positive(i:i64) -> Result<u64, String> {
  if i >= 0 {
    Ok(i as u64)
  } else {
    Err(String::from("負の整数です。"))
  }
}

【サンプルコードの解説】
main関数でpositive関数の引数に-10を渡しておりエラーになるためunwrap_orでデフォルト値10を取得します。
"正の整数にした場合10です。"をターミナルに表示します。

著者
大西 武 (オオニシ タケシ)
1975年香川県生まれ。大阪大学経済学部経営学科中退。プログラミング入門書など30冊以上を商業出版する作家。Microsoftで大賞やNTTドコモでグランプリなど20回以上全国区のコンテストに入賞するアーティスト。オリジナルの間違い探し「3Dクイズ」が全国放送のTVで約10回出題。
https://profile.vixar.jp

連載バックナンバー

開発言語技術解説
第15回

「TAURI」でデータベースを使ってみよう

2024/5/10
第15回の今回は「TAURI」でオープンソースのデータベース「SQLite3」を使用して、テーブル表に表示する解説をしていきます。
開発言語技術解説
第14回

「TAURI」で気象庁の「CSVデータ」を解析する

2024/5/1
第14回の今回は気象庁のWebサイトから指定した地域の1年間の気象データをダウンロードして「TAURI」で解析していきます。
開発言語技術解説
第13回

「TAURI」で「簡易RSSリーダー」を開発してみよう

2024/4/16
第13回の今回は「TAURI」で「RSSフィード」を読み込んでWebページに一覧表示し、リンクのページを開くための新規ウィンドウを作成するところまでを解説します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています