Help understanding interpolation rules

I can get the following code to pass

#[derive(thiserror::Error, Debug)]
#[error("`{0}` is invalid status. It must be one of ToDo, InProgress, or Done")]
pub struct TicketStatusError(String);

impl TryFrom<&str> for Status {
    type Error = TicketStatusError;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        match value.to_lowercase().as_str() {
            "todo" => Ok(Status::ToDo),
            "inprogress" => Ok(Status::InProgress),
            "done" => Ok(Status::Done),
            _ => Err(TicketStatusError(value.into())),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::convert::TryFrom;

    #[test]
    fn test_try_from_invalid() {
        let status = Status::try_from("Blah");
        assert!(status.is_err());
        assert_eq!(
            status.unwrap_err().to_string(),
            "`Blah` is invalid status. It must be one of ToDo, InProgress, or Done"
        )
    }
}

Here the interpolation is happening in the thiserror::Error macro's Display implementation (I hope I am correct about that). In the 2nd line of the above listing. The error value is likely TicketStatusError(String::from("Blah")).

But I want the error value to be TicketStatusError(String::from("Blah is invalid status. It must be one of ToDo, InProgress, or Done")). So I change the listing by making the following changes:

The Display implementation:

#[error("{0}")]

And the Err return in the match:

impl TryFrom<&str> for Status {
    fn try_from(value: &str) -> Result<Self, Self::Error> {
        match value.to_lowercase().as_str() {
            // ...
            _ => Err(TicketStatusError(
                "`{value}` is invalid status. It must be one of ToDo, InProgress, or Done".into(),
            )),
        }
    }
}

I get the error

assertion `left == right` failed
  left: "`{value}` is invalid status. It must be one of ToDo, InProgress, or Done"
 right: "`Invalid` is invalid status. It must be one of ToDo, InProgress, or Done"

Which means the interpolation of value into the error string did not happen. Please help me understand how to make this interpolation happen and what are the rules for interpolation here.

String literals in Rust are only string literals. "`{value}`" is literally the string "`{value}`".

You want format!().

2 Likes

:sweat_smile: It just occurred to me.

FYI, std's format specifiers (like "`{value}`...") are calculated at compile time via built-in macros. You always need a macro call somewhere to create them, versus a function call or just a bare "...".

(When it comes to non-std crates, you may also see the use of Arguments (ultimately also produced by the format_args! macro).)

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.