Return consumed argument on error

Description

If a fallible function consumes (moves) an argument, return that argument back inside an error.

Example

pub fn send(value: String) -> Result<(), SendError> {
    println!("using {value} in a meaningful way");
    // Simulate non-deterministic fallible action.
    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: {
        // Try to send value two times.
        for _ in 0..2 {
            value = match send(value) {
                Ok(()) => break 's true,
                Err(SendError(value)) => value,
            }
        }
        false
    };

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

Motivation

In case of error you may want to try some alternative way or to retry action in case of non-deterministic function. But if the argument is always consumed, you are forced to clone it on every call, which is not very efficient.

The standard library uses this approach in e.g. String::from_utf8 method. When given a vector that doesn’t contain valid UTF-8, a FromUtf8Error is returned. You can get original vector back using FromUtf8Error::into_bytes method.

Advantages

Better performance because of moving arguments whenever possible.

Disadvantages

Slightly more complex error types.

Last change: 2024-04-23, commit: 3719748