0

Trying to compile the following code:

#[derive(Show)]
pub enum E1 {
    A,
    B,
}
#[derive(Show)]
pub enum E2 {
    X(E1),
    Y(i32),
}

impl std::fmt::String for E1 {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        std::fmt::Show::fmt(self, f)
    }
}

impl std::fmt::String for E2 {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        std::fmt::Show::fmt(self, f)
    }
}

impl std::error::Error for E2 {
    fn description(&self) -> &'static str {
        match *self {
            E2::X(x) => {
                let d: &'static str = x.description();
                d
            },
            E2::Y(_) => "Unknown error",
        }
    }
}

impl std::error::Error for E1 {
    fn description(&self) -> &'static str {
        match *self {
            E1::A => "Error A",
            E1::B => "Error B",
        }
    }
}

fn main() { }

yields an error:

a.rs:17:39: 17:40 error: `x` does not live long enough
a.rs:17                 let d: &'static str = x.description();
                                              ^
note: reference must be valid for the static lifetime...
a.rs:15:9: 21:10 note: ...but borrowed value is only valid for the match at 15:8
a.rs:15         match *self {
a.rs:16             E2::X(x) => {
a.rs:17                 let d: &'static str = x.description();
a.rs:18                 d
a.rs:19             },
a.rs:20             E2::Y(_) => "Unknown error"
        ...
a.rs:15:15: 15:20 error: cannot move out of borrowed content
a.rs:15         match *self {
                      ^~~~~
a.rs:16:19: 16:20 note: attempting to move value to here
a.rs:16             E2::X(x) => {
                          ^
a.rs:16:19: 16:20 help: to prevent the move, use `ref x` or `ref mut x` to capture value by reference
a.rs:16             E2::X(x) => {
                          ^
error: aborting due to 2 previous errors

Changing the match pattern to E2::X(ref x) produces a perhaps more detailed error but leaves me just as confused:

a.rs:16:19: 16:24 error: cannot infer an appropriate lifetime for pattern due to conflicting requirements
a.rs:16             E2::X(ref x) => {
                          ^~~~~
a.rs:17:39: 17:40 note: first, the lifetime cannot outlive the expression at 17:38...
a.rs:17                 let d: &'static str = x.description();
                                              ^
a.rs:17:39: 17:40 note: ...so that pointer is not dereferenced outside its lifetime
a.rs:17                 let d: &'static str = x.description();
                                              ^
a.rs:15:9: 21:10 note: but, the lifetime must be valid for the match at 15:8...
a.rs:15         match *self {
a.rs:16             E2::X(ref x) => {
a.rs:17                 let d: &'static str = x.description();
a.rs:18                 d
a.rs:19             },
a.rs:20             E2::Y(_) => "Unknown error"
        ...
a.rs:16:19: 16:24 note: ...so that variable is valid at time of its declaration
a.rs:16             E2::X(ref x) => {
                          ^~~~~
error: aborting due to previous error

The way I see it, x only has to live until x.description() returns, but the compiler seems to thing it needs to outlive the whole match block. Why? Why does it also insist on treating x as a reference when copying would probably be more logical?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
dhardy
  • 11,175
  • 7
  • 38
  • 46

1 Answers1

2

As for x versus ref x, x won’t work because you have only a reference to self and so cannot move the E1 value out of it—all you can do is take a reference to it.

But now the more important thing: you’ve got your definition of the description method incorrect, and the Rust compiler isn’t warning you about that but is rather making life unpleasant for you.

This is the actual definition of the description method:

fn description(&self) -> &str;

Note carefully: &str, not &'static str. The compiler should have objected to the 'static in the signature, but alas, it did not. (This is the subject of https://github.com/rust-lang/rust/issues/21508, filed on account of this question.) Normally having specified a greater lifetime will be just fine, for it will just shrink it down to size, but in some situations it won’t do what you thought it would—specifically, it has changed E1’s description method to return a &str with its own lifetime, but in the E2 definition it is still wanting to return &'static str. Of course, the x reference is not 'static, so it fails to do it. Confusing, huh? Don’t worry, it’s mostly not your fault!

To fix this, remove all occurrences of 'static, to match the trait definition. Then because x is inside self the lifetimes will line up appropriately.

Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
  • Thanks. Still confused about one thing though: the `x` reference most definitely does not have long enough lifetime, but regardless the output of `x.description()` should have lifetime `'static`, no? Or does it not due to the changed lifetimes, but the compiler doesn't tell me this? – dhardy Jan 22 '15 at 13:45
  • @dhardy: your `description` method should not be dealing in `&'static str`s, and the fact that it partially “fixes” it for you (being lenient and allowing `'static` rather than objecting violently) is what’s tripping you up. – Chris Morgan Jan 22 '15 at 14:03
  • Yeah, so the compiler's demoted a lifetime without telling me about it. :( – dhardy Jan 22 '15 at 14:06
  • Thanks for saving me the trouble! – Chris Morgan Jan 22 '15 at 14:21