Confusing error message: Cannot call trait methods on &self implemented for self


#1

Hi there!

I just ran into a compiler error that really confused me. After some time I found out where my mistake was, but I think this could confuse many others. So here is some sample code: http://is.gd/UYaZFi

struct Foo;

impl Into<bool> for Foo {
    fn into(self) -> bool {
        true
    }
}

impl Foo {
    pub fn do_something(&self) {
        let b : bool = self.into();
    }
}

The compiler says:

<anon>:11:29: 11:35 error: the trait `core::convert::From<&Foo>` is not implemented for the type `bool` [E0277]
<anon>:11         let b : bool = self.into();
                                      ^~~~~~

So first of all the compiler automagically mentions From, which is kinda confusing, if one doesn’t know that Into is implemented for all Froms. Even if you know that, you still think that the compiler says “Into is not implemented for Foo”.
The real problem with the code is, that self in do_something is a reference, but into takes self by value.

I think its highly confusing. One is used to be able to call methods on references and values directly, thanks to auto-deref. Why isn’t this auto-dereffed here? Then there would be an error message like “cannot move out of borrowed value” which immediately points in the right direction.

So: Is this the way it should be? If yes: Why? If no: Are the language devs aware of that?


#2

Finding matching traits is tricky. I suspect that the compiler will go for the most probable requirement and use it in the error message. This case happened to make it choose From<&Foo> as the requirement of bool, which probably was chosen because of the blanket implementation you mentioned. I don’t know how true it is, but it’s my guess.

Now, remember that T and &T are not the same types, even if auto-ref and auto-deref makes them behave more like they were. This affects trait selection and probably makes it even harder to give intuitive information about what went wrong. Into<bool> could potentially be implemented for &Foo, so the compiler’s assumption that you are trying to use .into() from &Foo and not from Foo is completely valid. It could, however, also consider the implementation for Foo as an alternative when outputting the error. I just don’t know how feasible it would be, given the extra complexity, but dereferencing and checking for alternatives doesn’t sound too bad.