Broken error message? "(expected &Foo, found &Foo)"


#1

I was trying doing rando things w/ traits and this (I assume) broken message popped up. Is this a bug or am I missing something? (Also, the rustc --explain for E0495 doesn’t exist.)

I’m also not understanding the lifetime error it’s giving.

trait Foo {
        fn bar(&self);
}
trait FooDefault: Foo {
        fn xx(&self) {}
}
impl FooDefault for Foo { }

struct foo { }
impl Foo for foo {
        fn bar(&self) {}
}
// impl FooDefault for foo {}


fn obj(t : &Foo) {
        t.xx();
}

fn main() {
        let f = foo{};
        println!("done");
}

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> compiler-error2.rs:17:4
   |
17 |     t.xx();
   |       ^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the function body at 16:1...
  --> compiler-error2.rs:16:1
   |
16 | / fn obj(t : &Foo) {
17 | |     t.xx();
18 | | }
   | |_^
note: ...so that types are compatible (expected &Foo, found &Foo)
  --> compiler-error2.rs:17:4
   |
17 |     t.xx();
   |       ^^
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that types are compatible (expected FooDefault, found FooDefault)
  --> compiler-error2.rs:17:4
   |
17 |     t.xx();
   |       ^^

error: aborting due to previous error

#2

This looks like a lifetimes issue and the error message is eliding lifetimes to try and make it easier to read. The problem is that the elided lifetimes are actually important here. I assume your error would look something like Expected &'a Foo, found &'b Foo, which usually means something doesn’t live for long enough.

Either way this is probably a UI bug with error messages. You should create an issue for it.


#3

Thanks — do you know how to fix the obj function with lifetimes? The only way I can make it work is by passing in Box<Foo>

Which begs the question: why does that work either? In the code I’m implementing an additional dependent trait (FooDefault : Foo), and providing an “impl” for that via:

trait Foo {
        fn bar(&self);
}
trait FooDefault: Foo {
        fn xx(&self) { println!("ran xx"); }
}
// is it expected that this makes sense?
impl FooDefault for Foo { }

The code seems to “work” in that I’m able to impl Foo for a concrete struct type and then call the FooDefault method xx() on it. Is this legal? Or is it something that isn’t officially blessed that happens to slip through nightly?


#4

This kind of “blanket impl” is legal, intended to be, and frequently used as a way to extend other people’s types in your crate by implementing a custom trait on them :slight_smile:

Although usually, people write them using the generic syntax…

impl<T: Foo> FooDefault for T {}

…which makes it clear that the impl applies to any concrete type that implements Foo, and not to the trait Foo itself. (IIRC, your impl may mean something subtly different involving trait objects).


#5

As @HadrienG said, you’re implementing the trait for trait objects - not sure if that’s what you really want.

At any rate, trait objects have a 'static bound. You’d want to say impl<'a> FooDefault for Foo + 'a if you want to allow an arbitrary lifetime.


#6

As a follow-up brought up by thinking about @vitalyd’s post, note that for your example to work with the kind of blanket impl that I suggested, you will need to implement FooDefault for dynamically sized types too:

impl<T: Foo + ?Sized> FooDefault for T {}

I wrote a deeper discussion of what this syntax means and why it is needed elsewhere.

In your particular case, though, requesting the type to implement FooDefault before using xx() would probably be a simpler and equally valid strategy.


#7

BTW, to make the original code compile, you need to change the signature of obj():

fn obj(t : &(Foo + 'static)) {
        t.xx();
}

Box<Foo> works because that’s actually a Box<Foo + 'static>. This is all due to default object bounds.


#8

Oh, that’s cute — I didn’t realize you could do &(Foo + 'static)!


#9

BTW, the error in nightly is

error[E0621]: explicit lifetime required in the type of `t`
  --> src/main.rs:17:11
   |
16 | fn obj(t : &Foo) {
   |        - consider changing the type of `t` to `&'static Foo + 'static`
17 |         t.xx();
   |           ^^ lifetime `'static` required

#10

This is definitely an improvement :+1: I’m not sure suggesting &'static Foo makes sense though - that will almost certainly lead to a “doesn’t live long enough” error. Perhaps mention a lifetime parameter instead? Or suggest &(Foo + 'static).