Return self or error and re-use self in case of error

I have a method which consumes self and returns a modified version or fails. In case of failure I want the original self, but it's already moved. What would be the idiomatic way to handle this?

#[derive(Debug)]
struct Bla {
    a: i32,
}

impl Bla {
    fn transform(self) -> Result<Self, String> {
        if self.a > 1 {
            return Err("err!!!".to_string());
        }
        Ok(Self { a: self.a + 1 })
    }

    fn fix(mut self) -> Self {
        self.a -= 1;
        self
    }
}

fn main() {
    let b = Bla { a: 1 };
    let y = match b.transform() {
        Ok(x) => x,
        Err(e) => {
            println!("Got error: {}, fixing...", e);
            b.fix() // fails since b is moved!
        }
    };
    println!("{:?}", y);
}

You can mimic std::string::FromUtf8Error.
It has three methods:

  • utf8_error(&self) -> Utf8Error, which returns the internal error.
    Note that Utf8Error implements Copy trait.
  • as_bytes(&self) -> &[u8], which returns a reference to the original (source) data.
  • into_bytes(self) -> Vec<u8>, which returns the owned original data.

In your case, you may want to define custom error type (say BlaTransformError) as below, for example.

use std::fmt;

#[derive(Debug, Clone)]
struct BlaTransformError {
    bla: Bla,
    msg: String,
}

impl BlaTransformError {
    fn msg(&self) -> &str { &self.msg }
    fn as_bla(&self) -> &Bla { &self.bla }
    fn into_bla(self) -> Bla { self.bla }
}

impl fmt::Display for BlaTransformError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(&self.msg)
    }
}
impl std::error::Error for BlaTransformError {}
5 Likes

@lo48576 , thanks.
So basically the idea is to return a custom error type which encapsulates Self so I can extract self in my error processing. I see the logic in this.
Still it requires some overhead which I'm not sure is worth it in my case... If you come up with something even more concise I'd appreciate it.

I expect the overhead to be reasonably small, but I can't tell actually how much it costs.

If you don't want to return large result types, you can stop consuming self and define fn transform(&mut self) -> Result<(), SimpleError>.
This way transform() don't need to return Self on error, and the overhead is expected to be quite small.
However I don't know this interface is acceptable to you...

Another simple option is: fn transform(self) -> Result<Self, Self>

(But this doesn't let you include any additional information about the error.)