Disappointing compiler error message for missing impl From

This code

struct A(u32);
struct B(u32);

fn main() {
    let a = A(10);
    let b = B::from(a);
}

gives rise to this error

error[E0308]: mismatched types
 --> src/bin/play.rs:6:21
  |
6 |     let b = B::from(a);
  |             ------- ^ expected struct `B`, found struct `A`
  |             |
  |             arguments to this function are incorrect
  |
note: associated function defined here

For more information about this error, try `rustc --explain E0308`.

I wax lyrical about the wonderful compiler error messages in Rust, how they give you actionable advice and act as a Rust tutor. But this one is singularly unhelpful. I was hoping to get something more like

error[E0277]: the trait bound `B: From<A>` is not satisfied
 --> src/bin/play.rs:6:16
  |
6 |     let b: B = a.into();
  |                ^ ---- required by a bound introduced by this call
  |                |
  |                the trait `From<A>` is not implemented for `B`
  |
  = note: required for `A` to implement `Into<B>`

For more information about this error, try `rustc --explain E0277`.

which is what you get when you replace = B::from(a) with :B = a.into().

Why is the first message so unhelpful? Is there any hope that it might improve in the future?

1 Like

You can report this kind of thing as a bug to the compiler.

1 Like

I suspect, the compiler might be figuring out that for B::from only the genetic impl<T> From<T> for T matches, so this instance is used and the type of B::from becomes known to be a function with signature fn(B) -> B.

With this function signature in mind, the error message would make a lot of sense.

Consequence of the above considerations: If there was any other From ... for B implementations existing, the error message might change to something more like you expected.

I haven't tested these hypotheses, as I'm currently writing on a phone.


Regarding future improvements then, it seems somewhat difficult, since you wouldn't necessarily want to as far as to somehow identity as problematic (i. e. cause of the error) and then drop the knowledge of B::from's type signature, re-typecheck, and produce a nicer error about unmet trait bounds. On the other hand, maybe some special-case logic for improving the error message in straightforward examples like this, where the B::from signature very directly leads to the type mismatch, can be found.

Maybe somehow, the compiler could determine that (the relevant argument type of) the function B::from signature was only fully determined using some kind of “lack of other trait impls” reasoning, so at least a (more or less vague) hint that more From impls (of a specific shape the compiler could also tell you) could improve the situation could be generated.

It does indeed - see this (playground):

struct A(u32);
struct B(u32);

impl From<u32> for B {
    fn from(i: u32) -> B {
        B(i)
    }
}

fn main() {
    let a = A(10);
    let b = B::from(a);
}

Error:

error[E0277]: the trait bound `B: From<A>` is not satisfied
  --> src/main.rs:12:21
   |
12 |     let b = B::from(a);
   |             ------- ^ the trait `From<A>` is not implemented for `B`
   |             |
   |             required by a bound introduced by this call
   |
   = help: the trait `From<u32>` is implemented for `B`
1 Like

The proper way to deal with it would be to compare the argument type of B::from with the generic parameter of From<T>. The impl is parametric in the type of the argument, so it should say that an impl with a different parameter could enable calling B::from(a). I don't know whether the compiler has the required knowledge at the point the error is generated.

The compiler currently correctly describes its own reasoning: if there is a single impl, then it must apply. But that is not correct perspective for the user, who can affect the existing impls.

I made an issue for that.

3 Likes