rs.nkmk.me

RustでOptionをResultに変換

Posted: | Tags: Rust, エラー処理

RustでOptionResultに変換するにはok_or, ok_or_elseを使うか、anyhowライブラリを使う。関数内にOptionResultが混在している場合、OptionResultに変換することでどちらも呼び出し元に委譲できるようになる。

anyhowの方が簡単。anyhowの基本的な使い方については以下の記事を参照。

ok_or, ok_or_elseでOptionをResultに変換

ok_orOptionResultに変換できる。

ベクタの可変参照を受け取り、popで末尾の要素を取り出す処理を例とする。popOptionを返す(ベクタが空のとき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");

ここでは、簡単のため、文字列リテラルをそのままエラーとして返している。また、返り値に対してunwrapunwrap_errで値やエラーを取り出して確認しているが、実際のプログラムでは適切なエラー処理を行えばよい。以降の例も同じ。

上の例のように、関数内にOptionを返す処理しかない場合はOptionResultに変換する意味はない。

関数内にOptionResultが混在している場合にエラー型を独自に定義して対応する例などがRust By Exampleで紹介されている。

anyhowを使ってOptionResultに対応する例は後述。

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を使うとOptionanyhow::Resultに変換できる。NoneResultのエラーも?演算子で呼び出し元に委譲できるようになる。

本記事のサンプルコードにおけるanyhowのバージョンは以下の通り。

anyhow = "1.0"
source: Cargo.toml

use宣言は以下の通り。

use anyhow::{Context, Result};

anyhow::Contextトレイトをインポートすることで、ResultおよびOptioncontext, 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"
);

popNoneparseのエラーも呼び出し元に委譲できている。

なお、ok_orok_or_elseのように、contextは先行評価でwith_contextは遅延評価。詳細は以下の記事を参照。

関連カテゴリー

関連記事