Improving type mismatch errors


#1

I wanted together a quick post talking about an idea I was brainstorming with @brson and @nikomatsakis last week.

Background

For a type mismatch error, there often are a known set of steps to transition from one type to another. If it’s the first time the user has seen this error, they’re forced to go read docs to find the correct API call to do the conversion. This takes time and can be a bit frustration. Luckily, over time your muscle memory will kick in with the known conversion (eg String->&str or &str->String) and it will then just be a minor inconvenience to do the fix.

Improved errors

The idea is pretty simple: have the compiler search the methods on the type looking for a compatible self parameter as the one argument and the expected type as the return type.

For the &str to String case, for example, it may find:

fn to_string(&self) -> String

This can then become a suggestion we give the user. Whereas currently we say:

error[E0308]: mismatched types
 --> <anon>:4:17
  |
4 |   expect_string("foo");
  |                 ^^^^^ expected struct `std::string::String`, found reference
  |
  = note: expected type `std::string::String`
  = note:    found type `&'static str`

We could potentially say:

error[E0308]: mismatched types
 --> <anon>:4:17
  |
4 |   expect_string("foo");
  |                 ^^^^^ expected struct `std::string::String`, found reference
  |
suggestion:
  |   expect_string("foo".to_string());
  |                      ------------ convert to a String

Caveats

Can’t grab methods blindly

If we search for this pattern naively, we could potentially suggest methods that have side effects. While, yes, the correct type comes out, the dev could have inadvertently added a difficult to debug issue to their code in the process, just because the compiler told them to.

To prevent this, we may need to annotate methods with a “safe for suggestion” keyword that the suggestion logic can understand.

Multiple steps needed

Sometimes it’s more than just one step. While we don’t necessarily want to search deeply, we could do a limited number of steps. Assuming something like the “safe for suggestion” feature above, we’re in a small, bounded search space and still able to deliver helpful suggestions.

Supporting & and &mut

Since adding an & or &mut is a common task in Rust, we should also add them to the search space for each step (assuming the type is allowed to take that step)

Multiple possibilities

Sometimes there are multiple possibilities for the conversion. It may be through one step (like .to_string() and .into()) or different ways to multi-step to the right type. We’d need to come up with a heuristic here, eg) possibly showing a couple suggestions sorted by the by the shortest in terms of character length.

How do generics/traits factor into this?

I have no idea :slight_smile: This may be easier than it seems, I’m not sure. If it proves to be tricky, maybe we can support them in a later revision of the idea as we get more experience with it?


#2

Very cool idea! This is something that would be very useful even if it just worked for a very limited selection of methods (like, just those from std::convert), and &/&mut/*/ref. Especially the latter ones could help reduce the “Oh, I guess I need to put an ampersand there” feeling a lot of newcomers have.

One interesting challenge might be to deliver good suggestions in nested closures, where (in most of my code) everything relies on type inference and it’s easy to lose track of which type flat_map's argument currently has.


#3

I’m excited about this idea. I’d be happy to mentor someone – we should probably open an issue and I can try to layout a plan, but of course feel free to contact me if you’re interested. =)


#4

@nikomatsakis - done! https://github.com/rust-lang/rust/issues/37384