Result
これまでの例で、失敗する可能性のある関数の返り値として、列挙型Optionが使用でき、失敗時の返り値にはNoneを用いることを見てきました。しかし、時には なぜ そのオペレーションが失敗したのかを明示することが重要な場合があります。そのためにはResult列挙型を使用します。
列挙型Result<T, E>は2つの値をとりえます。
Ok(value)… これはオペレーションが成功したことを意味し、返り値valueをラップします。(valueは型Tを持ちます。)Err(why)… これはオペレーションの失敗を意味します。whyをラップしており、ここには失敗した理由が(必ずではありませんが)書かれています。(whyの型はEです。)
mod checked {
// 捕捉対象としたい、数学的な「エラー」
#[derive(Debug)]
pub enum MathError {
DivisionByZero,
NonPositiveLogarithm,
NegativeSquareRoot,
}
pub type MathResult = Result<f64, MathError>;
pub fn div(x: f64, y: f64) -> MathResult {
if y == 0.0 {
// 分母が0なので、このオペレーションは普通に行えば失敗します。
// 代わりに`Err`でラップされた失敗の理由を返しましょう。
Err(MathError::DivisionByZero)
} else {
// このオペレーションは問題がないので、
// 結果を`Ok`でラップして返しましょう。
Ok(x / y)
}
}
pub fn sqrt(x: f64) -> MathResult {
if x < 0.0 {
Err(MathError::NegativeSquareRoot)
} else {
Ok(x.sqrt())
}
}
pub fn ln(x: f64) -> MathResult {
if x <= 0.0 {
Err(MathError::NonPositiveLogarithm)
} else {
Ok(x.ln())
}
}
}
// `op(x, y)` === `sqrt(ln(x / y))`
fn op(x: f64, y: f64) -> f64 {
// 3段階の`match`ピラミッド!
match checked::div(x, y) {
Err(why) => panic!("{:?}", why),
Ok(ratio) => match checked::ln(ratio) {
Err(why) => panic!("{:?}", why),
Ok(ln) => match checked::sqrt(ln) {
Err(why) => panic!("{:?}", why),
Ok(sqrt) => sqrt,
},
},
}
}
fn main() {
// これは失敗するでしょうか?
println!("{}", op(1.0, 10.0));
}