-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
manual_let_else
should reuse the identifier from the original let statement
#9939
Comments
Just to note a corner case where both identifiers will need to be present: enum Foo {
Yes { v: i32 },
No,
}
let foo = Foo::Yes { v: 1 };
let value = match foo {
Foo::Yes { v } => v,
_ => return,
};
println!("We have a value of: {value}"); The current incorrect suggestion let Foo::Yes { v } = foo else { return }; will need to become let Foo::Yes { v: value } = foo else { return }; |
There is also the shadowing corner case, e.g.: enum Foo {
Yes { foo: u32, bar: u32 },
No,
}
let _foo = 42;
let bar = if let Foo::Yes { foo: _foo, bar } = make_foo() {
bar
} else {
return
};
println!("{_foo}"); You can't change that to: let Foo::Yes { foo: _foo, bar } = make_foo() else { return }; Because that might print a different The issue exists however only in code that is either receiving one of the |
A related example, if it helps: #![warn(clippy::pedantic)]
#![allow(clippy::unnecessary_wraps)]
use std::io::Result;
fn foo() -> Result<Option<(u32, u32)>> {
Ok(Some((1, 2)))
}
fn main() -> Result<()> {
let (x, y) = if let Some(pair) = foo()? {
pair
} else {
return Ok(());
};
println!("{x}, {y}");
Ok(())
} The suggestion removes the bindings for let Some(pair) = foo()? else {
return Ok(());
};
println!("{x}, {y}"); // `x`, `y` not found in this scope (Aside: I genuinely love this lint! ❤️) |
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.: let (_x, y) = if let _x @ Some(pair) = foo()? {
pair
} else {
return Ok(());
}; A And there is the further issue of the lint's tuple support. Often you can find people write code like: let (a, b) = if let Foo { a_res: Ok(a), b_opt: Some(b) } = foo() {
(a, b)
} else {
panic!()
}; People would employ this pattern in cases where they needed to build two bindings 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 let (a, b) = if let Foo { a_res: Ok(a), b_opt: Some(b) } = foo() {
(b, a)
} else {
panic!()
}; 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.
❤️ ❤️ 😄 |
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:
I expected to see this happen:
This is the suggestion it should emit, reusing the name
value
.Instead, this happened:
This suggestion causes an error:
Version
Additional Labels
@rustbot label +I-suggestion-causes-error
The text was updated successfully, but these errors were encountered: