エラー時に消費した引数を返す

説明

失敗する可能性のある関数が引数を消費(move)した場合、その引数をエラーの中に返却しましょう。

pub fn send(value: String) -> Result<(), SendError> {
    println!("using {value} in a meaningful way");
    // 非決定的な失敗し得るアクションをシミュレート。
    use std::time::SystemTime;
    let period = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap();
    if period.subsec_nanos() % 2 == 1 {
        Ok(())
    } else {
        Err(SendError(value))
    }
}

pub struct SendError(String);

fn main() {
    let mut value = "imagine this is very long string".to_string();

    let success = 's: {
        // value を2回送信してみる。
        for _ in 0..2 {
            value = match send(value) {
                Ok(()) => break 's true,
                Err(SendError(value)) => value,
            }
        }
        false
    };

    println!("success: {success}");
}

動機形成

エラーが発生した際、なにか別の手段を試したり、非決定的な関数であるならリトライしたくなるものです。しかし、もし引数が常に消費されてしまうのであれば、呼び出しのたびに引数をクローンしなければならなくなります。これではとても効率的とは言えません。

標準ライブラリでは、このアプローチを String::from_utf8 メソッドなどで使用しています。有効な UTF-8 を含まないベクタが与えられた場合、 FromUtf8Error を返します。 FromUtf8Error::into_bytes メソッドを使用することで、元のベクタを取得できます。

長所

可能な限り引数をmoveすることで、パフォーマンスを向上させます。

短所

エラーの型がやや複雑になります。

Last change: 2024-07-09, commit: 317c88e