The Most Common Rust Compiler Errors as Encountered in RustRover: Part 1

Read this post in other languages:

The Rust compiler is a picky creature. If it’s unhappy with the source code it’s fed, it can emit more than 400 different errors (with more being added every month!). Some of the errors are extremely rare, while others complicate the lives of Rust developers on a daily basis. In this blog post series, we’re going to look at the most common Rust compiler error messages that developers encounter in RustRover, a dedicated Rust IDE by JetBrains, and tips for how they can be avoided. But first, let’s see what we actually mean by “the most common errors”.

Identifying the most common errors from RustRover’s usage data

Any RustRover user may opt-in to enable sending their anonymous usage data to JetBrains. By analyzing this data, we can observe various user patterns and gain insights into how to improve the IDE. Of course we take your privacy seriously, so the IDE collects very limited information. For example, nothing in the data is ever traceable back to the user. Even with anonymized data, we can learn about IDE usage in general – like what kinds of error messages are generated most often.

Whenever a user who has opted-in initiates the Cargo Build command through the IDE (for example, by triggering a Run configuration that requires building a project) and the Rust compiler emits an error, we record an error code. This doesn’t include all of the code issues that come up as users write their code, but only those that remain after they build their project. The intermediate errors are often fixed by taking advantage of the inspections and quick-fixes that the IDE provides. The more users send us their usage data and the more often they use RustRover, the better we can understand their experience and the more we can improve the IDE’s code assistance features. So, thanks to everyone who opts-in and whose usage data helps us make RustRover better for all!

We collected the error codes from users running RustRover and ranked the errors based on how many users encounter them. This part of the series discusses the errors ranked 10th to 6th most common, and the next installment will reveal the top five most common ones. We’ll look at the reasons behind these errors, explore simple examples, and discuss potential ways to fix them.

Common error #10: E0412 (A used type name is not in scope)

Rust maintains a strict distinction between type declaration sites and type name usages. Every type name (including generic types) must be declared somewhere and be available in the scope in which it’s used. If the compiler comes across a type name usage but doesn’t have any information about its declaration site, it emits E0412. About 12% of RustRover users have experienced this error at some point.

Imagine you’ve typed i42 instead of i32. RustRover sees the problem and highlights the unknown type name. The compiler provides more details and suggests a fix, which can be easily applied by clicking the Apply fix button in the compiler output:

Some other situations that lead to E0412 include:

  • Forgetting to declare a type.
  • Importing a type to the current scope.
  • Introducing a generic type name and thus making the type inaccessible to the compiler.

To fix the issue, either provide a type declaration (declare a struct or introduce a generic type name properly) or bring the type to a scope (via the use clause). The official E0412 error explanation gives more examples of this error.

Common error #9: E0061 (An invalid number of arguments was passed when calling a function)

Even though RustRover is aware of this particular error and provides a set of fixes, 13% of RustRover users let it slip through before building their projects.

The error itself is pretty self-explanatory: We have a function that is either declared in the current scope or imported from somewhere else, and the call site gives too few or too many arguments. Let’s look at an example and compare RustRover’s suggestions with those of the Rust compiler:

In this example, we’re doing what many people are doing these days: opening a file. If we’re used to coding in another programming language, we might supply a second argument, forgetting that in Rust, this method takes only one. Both RustRover and the Rust compiler suggest removing the second argument. It’s nice that we didn’t even have to build a project to get that helpful suggestion from the IDE. Pay attention to those red wavy lines in your code – they’re usually onto something!

The situation gets more interesting if the function we’re calling is defined in our own code. Suppose we continue adding to the same code example:

In this case, RustRover suggests adding a parameter to the function as the first alternative, which may actually make sense here. The Rust compiler, though, insists on removing it. This difference has a rationale. The compiler’s job is to make sure that the program is correct, and the easiest way to achieve that is to get rid of the extra argument at the call site. The IDE’s goal, however, is to bring you closer to what you want to accomplish. If you’ve typed that argument for your own function, chances are that you meant it, so RustRover tries to help you finish what you started.

Common error #8: E0282 (The compiler could not infer a type and asked for a type annotation)

Sometimes the compiler is at a loss: It can’t figure out the type needed for a variable, but can only suggest adding a type annotation manually. If you’ve encountered this error before, welcome to the club – 13.5% of RustRover users have, too.

The main source of errors like E0282 is genericity. Many library functions take generic type parameters, but the compiler must instantiate those parameters to a concrete type and hence gets confused. Consider the following example:

Here we want to collect numbers from a string into some container. Unfortunately, the compiler cannot determine what type of numbers they are or what kind of container.

The compiler suggests specifying the container’s type first. However, if we apply this fix, we’ll get the same kind of error once again, now regarding str::parse. Both collect and parse are generic methods, but the compiler needs to know the precise type in order to compile the code that uses them. Note that RustRover doesn’t highlight any errors, as we’re still working on perfecting its type-checking functionality to help it figure this out.

There are several ways to fix the problem, because there’s more than one place to add a type annotation. We could specify the concrete type of the numbers vector:

let numbers: Vec<usize> = "1  5     6   3"

Or we could mention the same type when calling collect:

   .collect::<Vec<usize>>();

Finally, we could mention different types in different places:

let numbers = "1  5     6   3"
   .split_whitespace()
   .map(str::parse::<usize>)
   .map(Result::unwrap)
   .collect::<Vec<_>>();

This error is easily fixable: Just specify the type (or types) that you want.

Common error #7: E0432 (An import was unresolved)

RustRover provides plenty of autocomplete features. For example, let’s start by introducing a regular expression into our code:

If you choose the first suggestion, two more things are going to happen besides the completion itself:

  • A dependency on the regex crate will be added to your Cargo.toml.
  • A use regex::Regex; clause will be added at the top of the file.

When you add use clauses like that, the imports are written automatically and correctly. But  sometimes you need to write imports manually, and that’s when the E0432 error might come up. 15.5% of RustRover users experience it from time to time, most likely when they misspell a crate or module name, try importing something nonexistent, or let an erroneous use clause sneak into their code after copy-pasting it from somewhere. The first suggestion is always to check the dependencies and the names.

Sometimes RustRover can help prevent this error. If it is aware of the crate we are trying to import, it can either suggest adding a dependency when you paste code from external sources, or provide support via the following quick-fix:

Adding the corresponding dependency to Cargo.toml fixes the error immediately. Once the crate is available, it’s safer to use autocompletion for other path components in the use clauses to avoid more problems with names. Remember also that the availability of some names may depend on the enabled features of the crate.

There also may be issues with special path names like super or crate, especially in regard to treating them differently in different Rust editions. See more about that in the official explanation.

Common error #6: E0382 (A variable was used after its contents have been moved elsewhere)

Something interesting – issues with ownership! 17% of RustRover users have faced this error. The official explanation dives into great detail with many examples. Unfortunately, RustRover is not of great help here. If an external linter is disabled, RustRover’s internal machinery doesn’t see any issue with the following code:

fn main() {
   let vec = vec![1, 2, 3, 4, 5];
   let mut sum = 0;
   for v in vec {
       sum += v;
   }
   println!("Sum of {vec:?} elements is {sum}");
}

This innocent-looking code would be perfectly legitimate in a few other programming languages. We have a vector and want to compute the sum of its elements. If we’re coming from C, for example, and we don’t know all those fancy functional programming tricks with iterators, we write a traditional for loop instead. Once we’re done with summing everything up, we just print the vector and the result of the computations. Right?

Well, not in Rust, because it has what’s called ownership rules. 

The problem is that the source of data in the for loop is expanded to the into_iter() call, which takes ownership of the whole vector. Therefore, by the time we try to access the vector’s elements in println!, the compiler complains that it has been moved.

The fix is easy and is suggested by the compiler: Iterate over &vec to avoid moving it into the loop and borrow it instead.

In general, it’s helpful to always keep track of value ownership. Moving values and borrowing values are fundamental Rust concepts. Understanding them is every learner’s first order of business.

Summary

In this first part of our blog post series, we’ve defined the most common Rust compiler errors based on the usage data available in RustRover and discussed errors ranked from 10 to 6. In the next part, we’ll explore the top 5 most popular errors and also try to answer the question that’s on every Rust developer’s mind: “Which part of Rust is the most troublesome?”.

image description