Tryfrom with option and error

I have something like:

#[derive(Debug)]
pub struct InputData {
    pos_string: Option<String>,
}

#[derive(Debug)]
pub struct ParsedData {
    pos_string: Option<ParsedString>,
}

impl TryFrom<InputData> for ParsedData {
    type Error = String;
    fn try_from(value: InputData) -> Result<Self, Self::Error> {
        let pos_string = match &value.pos_string {
            Some(s) => Some(ParsedString::parse(s.to_string())?),
            None => None,
        };
        Ok(Self { pos_string })
    }
}

struct ParsedString(String);

impl ParsedString {
    fn parse(s: String) -> Result<Self, String> {
        if s == "string" {
            Ok(Self(s))
        } else {
            Err(format!("{} is not a valid string.", s))
        }
    }
}

Is there some nicer way of doing the part:

let pos_string = match &value.pos_string {
            Some(s) => Some(ParsedString::parse(s.to_string())?),
            None => None,
        };

Use Option::map followed by Option::transpose followed by a Result::map for the function body:

impl TryFrom<InputData> for ParsedData {
    type Error = String;
    fn try_from(value: InputData) -> Result<Self, Self::Error> {
        value
            .pos_string
            .map(|s| ParsedString::parse(s.to_string()))
            .transpose()
            .map(|s| Self { pos_string: s })
    }
}

How this works is:

  • First map converts the Option<String> to an Option<Result<ParsedString, String>>
  • The transpose converts Option<Result<ParsedString, String>> to Result<Option<ParsedString>, String>
  • The final map converts Result<Option<ParsedString>, String> to a Result<ParsedData, String>
2 Likes

Neither the original [1] nor @RedDocMD's solution need .to_string().

(After which you can remove the closure in the first map).


  1. if you remove the & in the match ↩ī¸Ž

2 Likes

Ah awesome. I didn't know about transpose, that's exactly what I want. Thanks!