Bad enforcement of clippy rule? - rust

I have disallowed unrelated shadowing in variable declarations.
But now this rule gives me an error on these two lines
let overflow: bool;
(self.standing, overflow) = self.standing.overflowing_add(reason.to_severity());
The linting error I get is:
error: `overflow` shadows a previous, unrelated binding
--> src/models/peer.rs:73:25
|
73 | (self.standing, overflow) = self.standing.overflowing_add(reason.to_severity());
| ^^^^^^^^
|
note: the lint level is defined here
--> src/lib.rs:1:9
|
1 | #![deny(clippy::shadow_unrelated)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/models/peer.rs:73:10
|
73 | (self.standing, overflow) = self.standing.overflowing_add(reason.to_severity());
| ^^^^^^^^^^^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
I'm using version 1.62.0 of the rust compiler.
My thoughts are that this behavior is a bug. The above lines should be allowed under this clippy rule. Am I wrong?
The problem can be seen here. Thanks to #Jmp for writing an this illustration of the problem.

This is a reported bug - rust-clippy#6141.
I think the problem is that the destructuring assignment is desugared into a let declaration that reuses the same span, and this causes clippy to think it has the same name and thus shadowing.

Related

Not understanding the match function of a HashMap.entry()

I am just starting to learn Rust. The entry method an Entry enum, which can be accessed via the match function. But then I don't get why it doesn't compile and it panics.
Can someone explain please what I'm understanding wrong?
let mut scores: HashMap<String, u32> = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
let a = 10;
match scores.entry("poneyland".to_string()) {
Occupied(o) => o.remove_entry(),
Vacant(o) => o.insert(37),
}
But then I don't get why it doesn't compile and it panics.
It can't both fail to compile and panic, since panic is a runtime behaviour. Either way, when there is an error it is usually a good idea to post it, as that makes diagnostic much easier, especially in Rust which generally has pretty helpful error messages.
In this case, the compilation error tells you exactly what's what:
error[E0308]: `match` arms have incompatible types
--> src/main.rs:15:22
|
13 | / match scores.entry("poneyland".to_string()) {
14 | | Occupied(o) => o.remove_entry(),
| | ---------------- this is found to be of type `(String, u32)`
15 | | Vacant(o) => o.insert(37),
| | ^^^^^^^^^^^^ expected tuple, found `&mut u32`
16 | | }
| |_____- `match` arms have incompatible types
|
= note: expected tuple `(String, u32)`
found mutable reference `&mut u32`
You pretty much just need to read the message, and possibly the expanded document.
What it boils down to is a rust match is an expression, meaning it has a value of a specific type, meaning all arms must have values of the same type (or a compatible type e.g. !).
Here, as the compiler error tells you, in one arm you're calling remove_entry which returns a tuple of (key, value), while in the other arm you're calling insert which returns a mutable reference to the inserted value.
These types are obviously not compatible.
The easiest way to fix this is to make them compatible by... suppressing the results entirely since you're not using them: convert the arm expressions to arm blocks and terminate the expressions with ;. That makes the expressions into statements and suppresses their value, implicitly returning () instead:
match scores.entry("poneyland".to_string()) {
Occupied(o) => {
o.remove_entry();
}
Vacant(o) => {
o.insert(37);
}
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1b885daa2a4eaa6d9d7c366753f6c2aa

Rust moving value into both sides of map_or_else?

Rust has a method on an enum called .map_or_else() it takes two closures one of which triggers on None, the other triggers on Some(inner) passing inner.
This is great, but what I want to do is move a value into both branches of it. This generates an error,
error[E0382]: borrow of moved value: `result`
--> src/sequence/renderer.rs:124:5
|
104 | let result = match self.display {
| ------ move occurs because `result` has type `String`, which does not implement the `Copy` trait
...
123 | || Ok(result),
| -- ------ variable moved due to use in closure
| |
| value moved into closure here
124 | |v| Ok(format!("{:0>width$}", result.clone(), width=v as usize ))
| ^^^ ------ borrow occurs due to use in closure
| |
| value borrowed here after move)
This error can be be solved by replacing the first closure to .map_or_else with,
Ok(result.clone())
But is this a good idea? Then I have to clone() a string for no benefit? What is the idiomatic way to use .map_or_else() when both sides need access to the same variable?
Unfortunately there is no way for this to work, since Rust has no way of annotating the two closures as "only one will get invoked".
So your best bet is to not use the higher-level function and instead write a simple match expression.

How to disable "unnecessary path disambiguator" warning?

I am generating code with a macro, which contains fully qualified type paths like this:
let vec: Vec::<String>;
Note the extra :: before <String>. This is necessary so that the same input token can be also be used for the constructor, by appending ::new():
Vec::<String>::new()
However, this produces warnings:
warning: unnecessary path disambiguator
--> src/main.rs:4:17
|
4 | let vec: Vec::<String>;
| ^^ try removing `::`
I can't remove the :: because then I get an error:
error: chained comparison operators require parentheses
--> src/main.rs:6:14
|
6 | vec = Vec<String>::new();
| ^^^^^^^^^^
|
= help: use `::<...>` instead of `<...>` if you meant to specify type arguments
= help: or use `(...)` if you meant to specify fn arguments
error[E0423]: expected value, found struct `Vec`
--> src/main.rs:6:11
|
6 | vec = Vec<String>::new();
| ^^^
| |
| did you mean `vec`?
| did you mean `Vec { /* fields */ }`?
How can I disable the warning just for this one line?
It is an open issue currently.
This lint is currently slipping these attributes like #![allow(warnings)]
Reference

Why can't I pass a String from env::Args to Path::new?

Consider the following example:
use std::env;
use std::path::Path;
fn main() {
let args: Vec<_> = env::args().collect();
let out_path: String = args[2];
let _path = Path::new(out_path);
}
Here's the error I'm getting while compiling:
error[E0308]: mismatched types
--> main.rs:8:27
|
8 | let _path = Path::new(out_path);
| ^^^^^^^^
| |
| expected reference, found struct `std::string::String`
| help: consider borrowing here: `&out_path`
|
= note: expected type `&_`
found type `std::string::String`
Now if I follow compiler's suggestion, I get this:
error[E0507]: cannot move out of indexed content
--> main.rs:7:28
|
7 | let out_path: String = args[2];
| ^^^^^^^
| |
| cannot move out of indexed content
| help: consider using a reference instead: `&args[2]`
error: aborting due to previous error
Which, after applying the suggestion, leads me to the previous error:
error[E0308]: mismatched types
--> main.rs:7:28
|
7 | let out_path: String = &args[2];
| ^^^^^^^^
| |
| expected struct `std::string::String`, found reference
| help: consider removing the borrow: `args[2]`
|
= note: expected type `std::string::String`
found type `&std::string::String`
How can I understand the situation and solve the problem?
This was indeed an unfortunate sequence of suggestions (use a reference > remove that reference), but this was caused by the manual type ascription related to out_path.
You want a string slice, not an owned String:
let out_path: &str = &args[2];
This fits both the restriction of args (you can't move out of indexed content) and the requirements of Path::new, which requires a reference.
As for your comment, a clone() "fixes" the cannot move out of indexed content error because it doesn't require a move from the args vector - it copies an element from it instead. This fix is of course inferior to just borrowing it, which also works with Path::new.

Use of Rust alignment feature (issue 33626)

RFC 1358 suggested an alignment attribute #[repr(align="N")] and it was accepted. Rust issue 33626 incorporated the feature into the nightly version.
I'm unable to use this feature with rustc 1.19.0-nightly (777ee2079 2017-05-01). If I compile without the feature gate (#![feature(repr_align)]):
#[repr(align="16")]
struct Foo {
bar: u32,
}
I get the following error statement:
error: the struct `#[repr(align(u16))]` attribute is experimental (see issue #33626)
--> foo.rs:3:1
|
3 | / struct Foo {
4 | | bar: u32,
5 | | }
| |_^
|
= help: add #![feature(repr_align)] to the crate attributes to enable
When I compile with the feature gate, the error message says:
error[E0552]: unrecognized representation hint
--> foo.rs:3:8
|
3 | #[repr(align="16")]
| ^^^^^^^^^^
I also tried the version suggested by the first error message (even though it does not comply with the issue), but still without success. What is the correct way to use the alignment feature?
You can make that feature work when combined with
attribute literals (Playground):
#![feature(repr_align)]
#![feature(attr_literals)]
#[repr(align(16))]
struct Foo {
bar: u32,
}
This is known to work in the latest development version (PR #41673). Searching "repr align" in the Rust compiler's codebase,
all occurrences rely on attribute literals, so it seems likely that the documented form repr(align="N") is not yet supported.

Resources