Implementing std::error::Error for a custom Struct

I was following along here: https://stevedonovan.github.io/rust-gentle-intro/6-error-handling.html#basic-error-handling

It contains an simplified example which seemed relevant, but my implementation doesn't lend itself well. The error here when compiling for description is: returns a reference to data owned by the current function . The example suggests using a specific attribute of a struct, which is a String. I think the reason that this is fine in the example because the compiler can know how long that struct will last. But what if we need to build the string at runtime? How do I make this work?

#[derive(Clone, Debug)]
pub enum ReadFailure {
    MissingFile(String),
    BadData(String),
    ...
}

impl fmt::Display for ReadFailure {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ReadFailure::MissingFile(path) => write!(f, "WARNING - File not read (file does not exist): {}", path),
            ReadFailure::BadData(path) => write!(f, "WARNING - File cannot be read (file has bad data): {}", path),
            ...
        }
    }
}

/// Implements an error for failure to read with a message
#[derive(Clone, Debug)]
pub struct ReadError {
    /// Failure condition
    error: ReadFailure,
}

impl fmt::Display for ReadError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.error)
    }
}

impl Error for ReadError {
    fn description(&self) -> &str {
        let s = format!("{}", self.error);
        &s
    }
}

Can you share the complete compile error you get?

Digging more, I think maybe I don't have to implement description...

The suggestion there is that I just need a Display implementation (or include a to_string). Since I already have that, I will just remove the Error block and retry.

This is the full error output...(it's not the only one, but it's the one that is relevant to the question above).

error[E0515]: cannot return reference to local variable `s`
  --> crates/ingest/src/read.rs:60:9
   |
60 |         &s
   |         ^^ returns a reference to data owned by the current function

Error::description is deprecated because its signature makes it basically impossible to return dynamic strings, making it a much-less-useful version of Display. You can omit it, or provide a stub implementation and assume that nobody will call it. (If they do call it, the compiler will warn them to use Display instead.)

impl Error for ReadError {
    fn description(&self) -> &str {
        "Read error"
    }
}
3 Likes

I have to create an implementation for the Error trait, but this seems wrong/non-idiomatic...an empty error implementation.

impl Error for ReadError {}

It's not the most common case, but it's not wrong. Some traits have zero required methods, and that's how it looks to implement them.

You can also use a crate like err-derive to replace some of the boilerplate impls for a custom error type.

1 Like