この記事は入門向けの記事になっています。
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
があります。