Closed
Description
Summary
The lint will emit an invalid suggestion when the binding name in the pattern differs from the name used in the let statement.
Reproducer
I tried this code:
#![warn(clippy::manual_let_else)]
fn main() {
let foo = Some(1);
let value = match foo {
Some(v) => v,
_ => return,
};
println!("We have a value of: {value}");
}
I expected to see this happen:
#![warn(clippy::manual_let_else)]
fn main() {
let foo = Some(1);
let Some(value) = foo else { return };
println!("We have a value of: {value}");
}
This is the suggestion it should emit, reusing the name value
.
Instead, this happened:
#![warn(clippy::manual_let_else)]
fn main() {
let foo = Some(1);
// It reuses the name `v`, which is not expected later on
let Some(v) = foo else { return };
println!("We have a value of: {value}");
}
This suggestion causes an error:
error[[E0425]](https://doc.rust-lang.org/nightly/error-index.html#E0425): cannot find value `value` in this scope
--> src/main.rs:8:36
|
8 | println!("We have a value of: {value}");
| ^^^^^ not found in this scope
Version
rustc 1.67.0-nightly (70f8737b2 2022-11-23)
binary: rustc
commit-hash: 70f8737b2f5d3bf7d6b784fad00b663b7ff9feda
commit-date: 2022-11-23
host: x86_64-unknown-linux-gnu
release: 1.67.0-nightly
LLVM version: 15.0.4
Additional Labels
@rustbot label +I-suggestion-causes-error
Activity
andersk commentedon Nov 28, 2022
Just to note a corner case where both identifiers will need to be present:
The current incorrect suggestion
will need to become
manual_let_else
produces wrong variable name #10171est31 commentedon Jan 11, 2023
There is also the shadowing corner case, e.g.:
You can't change that to:
Because that might print a different
_foo
value :).The issue exists however only in code that is either receiving one of the
unused
rustc lints, or code where you use a variable with a leading_
, which is a bit weird on its own. It's thus a corner case, but there is the possibility to change behaviour!smoelius commentedon Jan 30, 2023
A related example, if it helps:
The suggestion removes the bindings for
x
andy
:(Aside: I genuinely love this lint! ❤️)
est31 commentedon Jan 30, 2023
Oh yeah there are two more potential footguns to be aware of. First, as you point out correctly @smoelius , the let expression can also have an irrefutable pattern instead of bare identifiers. The code has to combine the two patterns. This is not trivial however, as the outer pattern can create unused bindings that overlap in name with the inner binding. Think e.g.:
A
let _x @ Some((_x, y)) = ... else { ... };
will not compile, it will complain about re-use of the binding. Admittedly this case might be rare but it's still a potential problem.And there is the further issue of the lint's tuple support. Often you can find people write code like:
People would employ this pattern in cases where they needed to build two bindings
a
andb
. Personally I think these cases are those which benefit the most from being changed to alet...else
, as "new data" is being created to emulate the feature.The lint recognizes the returned tuple as a "simple identity" and thus still fires (if there'd be a function call it wouldn't fire as it doesn't know if the function call has a side effect or not).
The issue is however that the order can be flipped, so instead
(b, a)
can be returned:Here a and b are flipped by the tuple returning. One can write code that respects this order change, it just needs to be kept in mind.
❤️ ❤️ 😄
manual_let_else
#15118Handle potentially-shadowing bindings in `manual_let_else` (#15118)