Not linting irrefutable_let_patterns on let chains by Natural-selection1 · Pull Request #146832 · rust-lang/rust (original) (raw)
Description
this PR makes the lint irrefutable_let_patterns not check for let chains,
only check for single if let, while let, and if let guard.
Motivation
Since let chains were stabilized, the following code has become common:
fn max() -> usize { 42 }
fn main() { if let mx = max() && mx < usize::MAX { /* */ } }
This code naturally expresses "please call that function and then do something if the return value satisfies a condition".
Putting the let binding outside the if would be bad as then it remains in scope after the if, which is not the intent.
Current Output:
warning: leading irrefutable pattern in let chain
--> src/main.rs:7:8
|
7 | if let mx = max() && mx < usize::MAX {
| ^^^^^^^^^^^^^^
|
= note: this pattern will always match
= help: consider moving it outside of the construct
= note: #[warn(irrefutable_let_patterns)] on by default
Another common case is progressively destructuring a struct with enum fields, or an enum with struct variants:
struct NameOfOuterStruct { middle: NameOfMiddleEnum, other: (), } enum NameOfMiddleEnum { Inner(NameOfInnerStruct), Other(()), } struct NameOfInnerStruct { id: u32, }
fn test(outer: NameOfOuterStruct) { if let NameOfOuterStruct { middle, .. } = outer && let NameOfMiddleEnum::Inner(inner) = middle && let NameOfInnerStruct { id } = inner { /* */ } }
Current Output:
warning: leading irrefutable pattern in let chain
--> src\main.rs:17:8
|
17 | if let NameOfOuterStruct { middle, .. } = outer
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this pattern will always match
= help: consider moving it outside of the construct
= note: #[warn(irrefutable_let_patterns)] on by default
warning: trailing irrefutable pattern in let chain --> src\main.rs:19:12 | 19 | && let NameOfInnerStruct { id } = inner | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this pattern will always match = help: consider moving it into the body
To avoid the warning, the readability would be much worse:
fn test(outer: NameOfOuterStruct) { if let NameOfOuterStruct { middle: NameOfMiddleEnum::Inner(NameOfInnerStruct { id }), .. } = outer { /* */ } }
related issue
possible questions
- Moving the irrefutable pattern at the head of the chain out of it would cause a variable that was intended to be temporary to remain in scope, so we remove it.
However, should we keep the check for moving the irrefutable pattern at the tail into the body? - Should we still lint
entire chain is made up of irrefutable let?
This is my first time contributing non-documentation code to Rust. If there are any irregularities, please feel free to point them out.
: )