25 Rustlings Clippy Solution

The Clippy tool is a collection of lints to analyze your code so you can catch common mistakes and improve your Rust code.

If you used the installation script for Rustlings, Clippy should be already installed. If not you can install it manually via rustup component add clippy.

Further information

Clippy1.rs

// clippy1.rs
// The Clippy tool is a collection of lints to analyze your code
// so you can catch common mistakes and improve your Rust code.
//
// For these exercises the code will fail to compile when there are clippy warnings
// check clippy's suggestions from the output to solve the exercise.
// Execute `rustlings hint clippy1` or use the `hint` watch subcommand for a hint.

// I AM NOT DONE

use std::f32;

fn main() {
    let pi = 3.14f32;
    let radius = 5.00f32;

    let area = pi * f32::powi(radius, 2);

    println!(
        "The area of a circle with radius {:.2} is {:.5}!",
        radius, area
    )
}

Our instructions are straight forward make the code compile and use Clippy's suggestions to make it happen. So let's look at the error's and suggestions.

Clippy1.rs Errors

Progress: [######################################################>-----] 86/94 (90.4 %)
⚠️  Compiling of exercises/clippy/clippy1.rs failed! Please try again. Here's the output:
    Checking clippy1 v0.0.1 (/Users/desmo/Projects/rustlings/exercises/clippy)
error: approximate value of `f32::consts::PI` found
  --> clippy1.rs:14:14
   |
14 |     let pi = 3.14f32;
   |              ^^^^^^^
   |
   = help: consider using the constant directly
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
   = note: `#[deny(clippy::approx_constant)]` on by default

error: could not compile `clippy1` (bin "clippy1") due to previous error
Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.

So Clippy doesn't like us using an approximate value of PI with 3.14f32 instead it suggests for us to use use the constant directly, there's even a link let's take a look at it in the solution.

Clippy2.rs Solution

We're given the approx_constant link above which if you click, I'll save you the trouble and summarize what is shows:

approx_constant

What it does

Checks for floating point literals that approximate constants which are defined in std::f32::consts or std::f64::consts, respectively, suggesting to use the predefined constant.

Why is this bad?

Usually, the definition in the standard library is more precise than what people come up with. If you find that your definition is actually more precise, please file a Rust issue.

Example

let x = 3.14;
let y = 1_f64 / x;

Use instead:

let x = std::f32::consts::PI;
let y = std::f64::consts::FRAC_1_PI;

Applying PI

We clearly see above that Clippy doesn't like us to use our own approximate value and wants us to import the std::f32::consts::PI from the standard library. So let's do that, but instead of using it in the variable, which is fine as they have done in the example, it might be easier to read and also use later in code if we import it with use and later just use PI in our variable...let's try that.

use std::f32::consts::PI;

fn main() {
    let pi = PI;
    let radius = 5.00f32;

    let area = pi * f32::powi(radius, 2);

    println!(
        "The area of a circle with radius {:.2} is {:.5}!",
        radius, area
    )
}

And with this we are compiling, of course you are free to use the other method of access PI directly by not importing it at the top of your code and then only modifying the pi variable like this:

let pi = std::f32::consts::PI;

Either way will compile and Clippy will be happy.

✅ Successfully compiled exercises/clippy/clippy1.rs!

🎉 🎉  The code is compiling, and 📎 Clippy 📎 is happy! 🎉 🎉

Clippy2.rs

// clippy2.rs
// Execute `rustlings hint clippy2` or use the `hint` watch subcommand for a hint.

// I AM NOT DONE

fn main() {
    let mut res = 42;
    let option = Some(12);
    for x in option {
        res += x;
    }
    println!("{}", res);
}

Here' we don't get any additional instructions but with a quick glance at the code, it seems that something doesn't seem quite right, but let's inspect the errors and see if we get additional details.

Clippy2.rs Errors

Checking clippy2 v0.0.1 (/Users/desmo/Projects/rustlings/exercises/clippy)
error: for loop over an `Option`. This is more readably written as an `if let` statement
 --> clippy2.rs:9:14
  |
9 |     for x in option {
  |              ^^^^^^
  |
  = note: `-D for-loops-over-fallibles` implied by `-D warnings`
  = help: to override `-D warnings` add `#[allow(for_loops_over_fallibles)]`
help: to check pattern in a loop use `while let`
  |
9 |     while let Some(x) = option {
  |     ~~~~~~~~~~~~~~~ ~~~
help: consider using `if let` to clear intent
  |
9 |     if let Some(x) = option {
  |     ~~~~~~~~~~~~ ~~~

error: could not compile `clippy2` (bin "clippy2") due to previous error

So, we're getting a clear error with:

`for loop over an `Option`. This is more readably written as an `if let` statement`

Clippy2.rs Solution

Because we have an Option here, we should use either if let or while let which essentially tells our code that if (or while) there is something inside of our option variable, we should then put that something inside of option and increment option by res in this case 42, if there is None then do nothing.

fn main() {
    let mut res = 42;
    let option = Some(12);

	  // updated with `if let` here
        if let Some(x) = option {
            res += x;
        }

    println!("{}", res);
}

if we were to print this we would see 54 as the output.

So with that updated if let our code compiles and we make Clippy happy again:

✅ Successfully compiled exercises/clippy/clippy2.rs!

🎉 🎉  The code is compiling, and 📎 Clippy 📎 is happy! 🎉 🎉

Clippy3.rs

// clippy3.rs
// Here's a couple more easy Clippy fixes, so you can see its utility.

// I AM NOT DONE

#[allow(unused_variables, unused_assignments)]
fn main() {
    let my_option: Option<()> = None;
    if my_option.is_none() {
        my_option.unwrap();
    }

    let my_arr = &[
        -1, -2, -3
        -4, -5, -6
    ];
    println!("My array! Here it is: {:?}", my_arr);

    let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5);
    println!("This Vec is empty, see? {:?}", my_empty_vec);

    let mut value_a = 45;
    let mut value_b = 66;
    // Let's swap these two!
    value_a = value_b;
    value_b = value_a;
    println!("value a: {}; value b: {}", value_a, value_b);
}

We're told that we have a couple of more easy Clippy fixes so we see how useful it is. Alright let's look at the errors.

⚠️  Compiling of exercises/clippy/clippy3.rs failed! Please try again. Here's the output:
    Checking clippy3 v0.0.1 (/Users/desmo/Projects/rustlings/exercises/clippy)
error: possibly missing a comma here
  --> clippy3.rs:14:19
   |
14 |         -1, -2, -3
   |                   ^
   |
   = note: to remove this lint, add a comma or write the expr in a single line
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
   = note: `#[deny(clippy::possible_missing_comma)]` on by default

error: this call to `unwrap()` will always panic
  --> clippy3.rs:10:9
   |
9  |     if my_option.is_none() {
   |        ------------------- because of this check
10 |         my_option.unwrap();
   |         ^^^^^^^^^^^^^^^^^^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
   = note: `#[deny(clippy::panicking_unwrap)]` on by default

error: this looks like you are trying to swap `value_a` and `value_b`
  --> clippy3.rs:25:5
   |
25 | /     value_a = value_b;
26 | |     value_b = value_a;
   | |_____________________^ help: try: `std::mem::swap(&mut value_a, &mut value_b)`
   |
   = note: or maybe you should use `std::mem::replace`?
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
   = note: `#[deny(clippy::almost_swapped)]` on by default

error: used `unwrap()` on `None` value
  --> clippy3.rs:10:9
   |
10 |         my_option.unwrap();
   |         ^^^^^^^^^^^^^^^^^^
   |
help: remove the `None` and `unwrap()`
  --> clippy3.rs:8:33
   |
8  |     let my_option: Option<()> = None;
   |                                 ^^^^
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap
   = note: `-D clippy::unnecessary-literal-unwrap` implied by `-D warnings`
   = help: to override `-D warnings` add `#[allow(clippy::unnecessary_literal_unwrap)]`

error: this let-binding has unit value
  --> clippy3.rs:19:5
   |
19 |     let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `vec![1, 2, 3, 4, 5].resize(0, 5);`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
   = note: `-D clippy::let-unit-value` implied by `-D warnings`
   = help: to override `-D warnings` add `#[allow(clippy::let_unit_value)]`

error: could not compile `clippy3` (bin "clippy3") due to 5 previous errors

Alright looks like we have a lot of work to do here going through each of them one by one and see how we can get this code to compile.

Clippy3.rs Solution

#[allow(unused_variables, unused_assignments)]
fn main() {
    let my_option: Option<()> = None;
    if my_option.is_none() {
        my_option.unwrap();
    }

    let my_arr = &[
        -1, -2, -3
        -4, -5, -6
    ];
    println!("My array! Here it is: {:?}", my_arr);

    let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5);
    println!("This Vec is empty, see? {:?}", my_empty_vec);

    let mut value_a = 45;
    let mut value_b = 66;
    // Let's swap these two!
    value_a = value_b;
    value_b = value_a;
    println!("value a: {}; value b: {}", value_a, value_b);
}

Error 1

Our first error focuses on my_arr where it seems like we're missing a comma.

error: possibly missing a comma here
  --> clippy3.rs:14:19
   |
14 |         -1, -2, -3
   |                   ^
   |
   = note: to remove this lint, add a comma or write the expr in a single line
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
   = note: `#[deny(clippy::possible_missing_comma)]` on by default

So by adding the comma after the -3, this issue is fixed.

Error 2

This error is highlighting a misuse of the unwrap() method on an Option type. Specifically, it's warning against calling unwrap() on an Option that is known to be None, as this will always cause a panic at runtime.

error: this call to `unwrap()` will always panic
  --> clippy3.rs:10:9
   |
9  |     if my_option.is_none() {
   |        ------------------- because of this check
10 |         my_option.unwrap();
   |         ^^^^^^^^^^^^^^^^^^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
   = note: `#[deny(clippy::panicking_unwrap)]` on by default

This code checks if my_option is None, and if so, attempts to unwrap it, which is exactly the pattern Clippy is advising against. It's logically inconsistent to check for None and then unwrap it, as unwrap is meant for situations where you are sure the Option is Some. So what do we do, just remove the if check so this error goes away.

Error 3

Here Clippy understands that we're trying to swap values and advises us to use std::mem::swap. Again, I prefer to use the use key word to import just to keep the code a little bit cleaner.

error: this looks like you are trying to swap `value_a` and `value_b`
  --> clippy3.rs:25:5
   |
25 | /     value_a = value_b;
26 | |     value_b = value_a;
   | |_____________________^ help: try: `std::mem::swap(&mut value_a, &mut value_b)`
   |
   = note: or maybe you should use `std::mem::replace`?
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
   = note: `#[deny(clippy::almost_swapped)]` on by default

So our updated code would look like this.

// import at the top of the file
use std::mem::swap;

// -- snippet -- //
// Swapping Values

let mut value_a = 45;
let mut value_b = 66;

// Let's swap these two!
swap(&mut value_a, &mut value_b);

println!("value a: {}; value b: {}", value_a, value_b);

Error 4

Our final error is related to wanting to create an empty Vec it looks like currently we are creating and then resizing, which is a waste of effort if all we want is an empty Vector.

error: this let-binding has unit value
  --> clippy3.rs:19:5
   |
19 |     let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `vec![1, 2, 3, 4, 5].resize(0, 5);`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
   = note: `-D clippy::let-unit-value` implied by `-D warnings`
   = help: to override `-D warnings` add `#[allow(clippy::let_unit_value)]`

There's a couple of ways to solve it but probably the easiest is to just remove the elements from the vector and the .resize() method at the end.

let my_empty_vec: Vec<i32> = vec![];

We're also asked to define it's type but that's pretty straightforward with Vec<i32> right before the empty vec![] macro. Here's the final code for reference.

use std::mem::swap;
#[allow(unused_variables, unused_assignments)]
fn main() {
    let my_option: Option<()> = None;

    // array
    let my_arr = &[
        -1, -2, -3,
        -4, -5, -6
    ];
    println!("My array! Here it is: {:?}", my_arr);

    // Create empty Vector
    let my_empty_vec: Vec<i32> = vec![];
    println!("This Vec is empty, see? {:?}", my_empty_vec);

    // Swapping Values
    let mut value_a = 45;
    let mut value_b = 66;

    // Let's swap these two!
    swap(&mut value_a, &mut value_b);

    println!("value a: {}; value b: {}", value_a, value_b);
}

with these changes Clippy is once again a happy camper:

✅ Successfully compiled exercises/clippy/clippy3.rs!

🎉 🎉  The code is compiling, and 📎 Clippy 📎 is happy! 🎉 🎉

Conclusion

In conclusion, navigating through Rust's Clippy lints is not only an exercise in resolving compile-time errors but also a journey into the depths of idiomatic Rust programming. Clippy, as a linter, serves a pivotal role in guiding developers towards best practices, ensuring code reliability, and maintaining the high standards of Rust's safety guarantees.

Through the examples of clippy1.rs, clippy2.rs, and clippy3.rs, we saw common pitfalls and learned how to address them effectively. Whether it was using the precise value of PI in clippy1.rs, embracing the if let construct for Option types in clippy2.rs, or understanding the importance of proper array syntax and variable swapping in clippy3.rs, each scenario brought its unique learning experience.

These exercises underscore the importance of attention to detail and the nuances of the Rust language. Clippy acts not just as a linter but as an educator, reinforcing the Rust philosophy of writing clear, efficient, and safe code. As Rust continues to evolve, tools like Clippy will remain invaluable for developers, whether they are beginners just starting their Rust journey or experienced coders refining their craft. In the world of Rust, Clippy is more than a tool; it's a companion on the path to mastering one of the most promising systems programming languages of our time.