I'm trying to use a String that is part of an Error in it's description() method.
Edit: Note this is a simplified toy example demonstrating the issue.
use std::error::Error;
use std::fmt;
#[derive(Debug)]
enum NamedError {
Name(String),
}
impl fmt::Display for NamedError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
NamedError::Name(ref name) => write!(f, "My name is: {}", name),
}
}
}
impl Error for NamedError {
fn description(&self) -> &str {
match *self {
NamedError::Name(ref name) => format!("My name is: {}", name),
}
}
fn cause(&self) -> Option<&Error> {
None
}
}
fn main() {
let err = NamedError::Name("Arnold".to_owned());
}
But I get a compile error:
$ cargo run
Compiling rust-test v0.1.0 (file:///Users/hagenjt1/PycharmProjects/rust-test)
error[E0308]: match arms have incompatible types
--> src/main.rs:19:9
|
19 | / match *self {
20 | | NamedError::Name(ref name) => format!("My name is: {}", name),
21 | | }
| |_________^ expected &str, found struct `std::string::String`
|
= note: expected type `&str`
found type `std::string::String`
note: match arm with an incompatible type
--> src/main.rs:20:43
|
20 | NamedError::Name(ref name) => format!("My name is: {}", name),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in a macro outside of the current crate
error: aborting due to previous error
error: Could not compile `rust-test`.
To learn more, run the command again with --verbose.
Process finished with exit code 101
I tried adding a .to_str() but then the reference doesn't live long enough. Is there an easy way to solve this?
No, there's no way to return that. However, I think you're fine anyway. description can be some static text, and then your callers/users of the NamedError can use its Display impl if they want the more detailed information.
String is equivalent to C's malloc()-ed string that needs to be free()d, and &str is const char * that can be anything (on stack, in ROM, etc.)
In C that would be:
/** Do *not* call free() on this pointer */
const char *description(const Self *self);
And your question becomes equivalent of "how can I use malloc() in description and return it as a pointer that is never free()d?". So the only options are return something that's already in self, or leak memory.
In rust-lang-nursery/api-guidelines#71 we determined that description() should basically never be called. So don't worry about putting a useful message there. For example serde_json's error type just returns a description that is "JSON error".
What I'm gathering is that if a function returns -> &str it really is returning &'static str (which is elided). Thus it must return a 'static string literal to satisfy this.
Not quite in this case - fn description(&self) -> &str returns either a reference that's tied to the lifetime of self or a 'static reference. So the non-elided signature is fn description<'a>(&'a self) -> &'a str. But 'static is a subtype of 'a (i.e. a longer lifetime can be substituted for a shorter one), so you can also return 'static there. Note this subtype relationship is true only for immutable references.
If you had fn bar() -> &str then that really is fn bar() -> &'static str because there's no input lifetime to associate with the output one.
You could, if you really wanted to, embed the entire error message into the String inside NamedError::Name. Then you could return a reference to that. But as mentioned, it's unnecessary really and will distort the meaning of Name.