RustでOptionをResultに変換
RustでOptionをResultに変換するにはok_or, ok_or_elseを使うか、anyhowライブラリを使う。関数内にOptionとResultが混在している場合、OptionをResultに変換することでどちらも呼び出し元に委譲できるようになる。
anyhowの方が簡単。anyhowの基本的な使い方については以下の記事を参照。
- 関連記事: Rust, anyhowによる基本的なエラー処理
ok_or, ok_or_elseでOptionをResultに変換
ok_orでOptionをResultに変換できる。
ベクタの可変参照を受け取り、popで末尾の要素を取り出す処理を例とする。popはOptionを返す(ベクタが空のときNone)。
?演算子で呼び出し元に委譲する。
fn pop_ok_or(v: &mut Vec<&str>) -> Result<String, &'static str> {
let s = v.pop().ok_or("Vector is empty")?;
Ok(String::from(s))
}
let mut v = vec!["10", "20"];
assert_eq!(pop_ok_or(&mut v).unwrap(), "20");
let mut v = vec![];
assert_eq!(pop_ok_or(&mut v).unwrap_err(), "Vector is empty");
ここでは、簡単のため、文字列リテラルをそのままエラーとして返している。また、返り値に対してunwrapやunwrap_errで値やエラーを取り出して確認しているが、実際のプログラムでは適切なエラー処理を行えばよい。以降の例も同じ。
上の例のように、関数内にOptionを返す処理しかない場合はOptionをResultに変換する意味はない。
関数内にOptionとResultが混在している場合にエラー型を独自に定義して対応する例などがRust By Exampleで紹介されている。
anyhowを使ってOptionとResultに対応する例は後述。
ok_orとok_or_else
ok_orは先行評価で、ok_or_elseは遅延評価。関数の結果を渡すような場合、ok_or_elseの使用が推奨されている。
例えば、ok_or(format!(...))ではなくok_or_else(|| format!(...))。ok_orは常にformat!が実行されてしまうが、ok_or_elseだとNoneのときのみformat!が実行される。|| format!(...)は引数なしのクロージャ。
*_orと*_or_elseとの違いについては以下の記事を参照。
anyhowでOptionをResultに変換
anyhowを使うとOptionをanyhow::Resultに変換できる。NoneもResultのエラーも?演算子で呼び出し元に委譲できるようになる。
本記事のサンプルコードにおけるanyhowのバージョンは以下の通り。
anyhow = "1.0"
use宣言は以下の通り。
use anyhow::{Context, Result};
anyhow::Contextトレイトをインポートすることで、ResultおよびOptionでcontext, with_contextメソッドが使えるようになる。
ベクタの可変参照を受け取り、popで末尾の要素を取り出し、parseで数値に変換する関数を例とする。popはベクタが空のときNoneを返し、parseは変換できないときエラーを返す。
fn pop_parse_anyhow(v: &mut Vec<&str>) -> Result<i32> {
let s = v.pop().context("Vector is empty")?;
let i = s.parse::<i32>()?;
Ok(i)
}
let mut v = vec!["10", "20"];
assert_eq!(pop_parse_anyhow(&mut v).unwrap(), 20);
let mut v = vec![];
assert_eq!(
pop_parse_anyhow(&mut v).unwrap_err().to_string(),
"Vector is empty"
);
let mut v = vec!["abc", "xyz"];
assert_eq!(
pop_parse_anyhow(&mut v).unwrap_err().to_string(),
"invalid digit found in string"
);
popのNoneもparseのエラーも呼び出し元に委譲できている。
なお、ok_orとok_or_elseのように、contextは先行評価でwith_contextは遅延評価。詳細は以下の記事を参照。
- 関連記事: Rust, anyhowによる基本的なエラー処理