この記事は入門向けの記事になっています。
Rustが進化して行くに連れて、バージョン1以降になってからも変化がありました。個人的にその変化の一つが ?オペレータの登場です。
fn foo() -> Result<(), String> {
Err("error".to_owned())?;
println!("no print");
Ok(())
}
println!("{:?}", foo());
このオペレータは、try!マクロと同様の動きをします。Rustの多くのプログラマは、このオペレータが登場する前にtry!マクロを活用していました。これからは?が多く使われることでしょう。
他の言語では、? はOptionalとして扱われるケースが多いため、Rustの場合は例外時にreturnされることと、OptionではなくResultであることに注意が必要です。
Option型は ? が使えない
さて、ここで本題に入ります。Rustのcrateによっては、ResultではなくOptionで結果を返すライブラリも多くあります。Optionの場合は、? を使うことが出来ないため、Resultに変換する必要があります。
追記(2018/03/05) 使えるようになっています。
ここから先にいくつか悪い例を紹介し、最後に良い例を紹介します。
matchで変換
fn foo() -> Result<(), String> {
let opt: Option<String> = None;
match opt {
Some(s) => Ok(s),
None => Err("error".to_owned())
}?;
println!("no print");
Ok(())
}
println!("{:?}", foo());
型でマッチさせたあとにResultを作っています。ちょっと長いです。
unwrap系(動かない)
fn test() {
fn foo() -> Result<(), String> {
let opt: Option<String> = None;
opt.unwrap_or(Err("error".to_owned()))?;
println!("no print");
Ok(())
}
println!("{:?}", foo());
こちらは、 expected struct `std::string::String`, found enum `std::result::Result` が発生し失敗します。
unwrap系は、成功した場合、self を返すため、型がどうしてもOptionになってしまいます。
ok_or
fn foo() -> Result<(), String> {
let opt: Option<String> = None;
opt.ok_or("error".to_owned())?;
println!("no print");
Ok(())
}
println!("{:?}", foo());
Optionには、ok_or<E>(self, err: E) があり、self が Option<T> から Result<T, E> に変換されて返されます。OptionはErrを持っていないため、Resultに変換する際に引数をErrとして渡しているところがポイントです。
OptionをResultに変換する場合は、ok_or を使うとスッキリ書くことが出来ます。他にも、ファンクションが渡せる ok_or_else があります。