-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Spurious irrefutable_let_patterns warning with let-chain #139369
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
another example: #[allow(irrefutable_let_patterns)]
if let errCode = GetLastError() && errCode != ERROR_NO_MORE_ITEMS {
let _ = dbg!(errCode);
// logging the errCode to db, etc
} Honestly, I don't want to introduce the |
Do you want to weaken or remove this lint in general, or do you just want it to not fire on let chains? |
I think there's a reasonable case for not applying it to let chains, specifically. In the absence of let chains, I seem to recall at some point that we talked about making it not fire for any subsequent let in the chain, but keeping it for the first one since that one can be moved out. (But, of course, that's not true for |
That works for me. |
Both of the examples posted above argue against that: moving it out extends the scope of the binding, which might not be desired. |
If someone wants to put up a PR to make it not fire on let chains and nominate that, I'll propose FCP merge on it. I'd estimate that being likely to save us a step here. |
@RalfJung I'm aware, but that isn't necessarily a reason to allow it, just an argument that someone might want to write it that way. The downside of allowing it for let chains in general is that it may not be intentional, and sometimes this catches errors where you meant to write a refutable pattern. But nonetheless, I'd be 👍 for allowing it for let chains, for now, with the possibility of coming up with something more specific in the future to catch potential mistakes. |
Fair. It just seemed odd to make a suggestion that would change nothing for 100% of the examples considered in this issue. |
@rustbot labels -I-lang-nominated +I-lang-radar We talked about this in the @rust-lang/lang call today. We decided we just wanted to rip out the And CC @rust-lang/clippy in the event that clippy might want to add something here. |
I find the #![allow(irrefutable_let_patterns)]
fn foo() -> Option<u32> {
None
}
fn main() {
if let x = foo() {
println!("{:?}", x);
}
} This prints In most cases this mistake will lead to a type error really quickly because the Option isn't unwrapped in the body of the if statement -- I had to use |
We did talk about how some aspects of the lint might be most beneficial to new users, and that's why we pinged the clippy team here, as adding a lint to help such users might better fit in their domain. |
The comparison to Swift is interesting; I'd forgotten about that syntax. The specific case of That said, with example from @jnkr-ifx I've switched back to thinking most non-chain uses of What pushed me toward removing the lint is that I don't want overly paternalistic lints that limit expressiveness needlessly. It's true that we've already experienced that with some of the "irrefutable" lints (particularly in the where clause department where we've already relaxed it some). In fact I felt some annoyance about this arising in the discussion today. But I also don't buy the case I've heard so far for how it's nice to support if let Some(x) = foo() {
...
} else if let Ok(y) = bar() {
...
} else if let z = baz() {
...
} And I don't know, maybe I would want to write that for aesthetic reasons, but it isn't so bad to write In contrast, I find the example with a let chain brought up by @kurikomoe extremely motivating. Assigning to a variable and checking a condition on its value immediately is a very common pattern, and it's also common to only need that variable to exist inside the if block. Other languages have forms like this for the same reason. Outside of let chains, my thinking now is this is one of the places where Rust should err more in the direction of Go (simple and uniform) than Haskell (elegant and inscrutable). In terms of design goals, I would argue that "clarity of purpose" and "correctness by construction" are both on the side of keeping the lint but removing it for let chains. |
In that case, I feel like the lint you might want is about a trivially true condition rather than about an irrefutable pattern. These all seem about the same to me: if let () = () { .. }
if let () = () && true { .. }
if let () = () && 0 == 0 { .. }
if true { .. }
if 0 == 0 { .. } Similarly, a trailing |
We're already convinced on let chains, but for posterity, here's what the most motivating case for that looks like to me: if let x = op_one()
&& check_one(x)
&& let y = op_two(x)
&& check_two(y)
&& let z = op_three(y)
&& check_three(z)
{
..
} That is, we don't want to get |
I don't agree for |
Yes, that's actually why I feel the same about So I don't really see a principled or practical case for distinguishing an irrefutable let expression from any other trivially true expression. If we want to nag people to unwrap these blocks (or at least remove the |
Generally speaking, if I'm in the middle of debugging, and have deleted some code or added |
Code
Current output
Desired output
Rationale and extra context
This code naturally expresses "please call that function and then do something if the return value satisfies a condition". Putting the
let
binding outside theif
would be bad as then it remains in scope after theif
, which is not the intent. I could wrap it in an extra layer of curly braces but that increases the overall indentation level.I think this warning should never trigger for let chains.
Other cases
Rust Version
Anything else?
No response
The text was updated successfully, but these errors were encountered: