Skip to content
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

RFC: let-expression #3159

Closed
wants to merge 19 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions text/0000-let-expression.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,46 @@ assert!(matches!(a, Some(x) if let Some(y) = b(x) && x == y));
assert!(if let Some(x) = a && let Some(y) = b(x) && x == y { true } else { false });
```

### Practical usage of this features

People find let expressions theoretical at first look, and only traditional cases (if-let-chain and let-else)
Can become useful. In this section there are some usages for new features of this RFC in real codes.

This is an example from rust-clippy repository:
```rust
for w in block.stmts.windows(2) {
if_chain! {
if let StmtKind::Semi(first) = w[0].kind;
if let StmtKind::Semi(second) = w[1].kind;
if !differing_macro_contexts(first.span, second.span);
if let ExprKind::Assign(lhs0, rhs0, _) = first.kind;
if let ExprKind::Assign(lhs1, rhs1, _) = second.kind;
if eq_expr_value(cx, lhs0, rhs1);
if eq_expr_value(cx, lhs1, rhs0);
then {
// 30 lines of code with massive rightward drift
}
}
}
```

Which by a generalized let-else can become:
```rust
for w in block.stmts.windows(2) {
(let StmtKind::Semi(first) = w[0].kind)
&& (let StmtKind::Semi(second) = w[1].kind)
&& !differing_macro_contexts(first.span, second.span)
&& (let ExprKind::Assign(lhs0, rhs0, _) = first.kind)
&& (let ExprKind::Assign(lhs1, rhs1, _) = second.kind)
&& eq_expr_value(cx, lhs0, rhs1)
&& eq_expr_value(cx, lhs1, rhs0)
|| continue;
// 30 lines of code with two less tab
Copy link
Member

@kennytm kennytm Aug 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is more like a practical example of the existing if_let_chains feature (if you must include a continue for the number of indentations)

    if let StmtKind::Semi(first) = w[0].kind 
        && let StmtKind::Semi(second) = w[1].kind
        && !differing_macro_contexts(first.span, second.span)
        ...
        && eq_expr_value(cx, lhs1, rhs0)
    {
    } else {
        continue;
    }

this is also more consistent with the way people would write

if next_condition {
    continue;
}

rather than

!next_condition || continue;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if-let-chain still need one indentation, because variables are bound inside of if body (like if-let) but in the example with || variables are bound after statement (like let-else). If you believe if-let can't meet the needs that let-else provide, The same statement is true for if-let-chain and this RFC.

this is also more consistent with the way people would write

I am agree and prefer explicit if over || in normal cases. Without bindings, both behavior is the same (both in current rust and in this RFC) but because binding rules for them are different (for good reasons; for example accessing if-let variables after body is surprising) you can save one indentation by || one.

And it is worth noting that !next_condition || continue; is completely valid and working in today rust. But !next_condition else { continue }; isn't and won't, so let-else syntax has a negative point here and this RFC is more consistent with rust.

}
```
Every `if let` or `if_chain!` that fill body of a loop or function can refactored in this way. You can easily find
dozens of them just in rust-clippy.

## Why now?
This RFC exists thanks to people who choose `if let` for syntax we know today.
That syntax wasn't the only choice and there was other options like `iflet`, `if match`, `let if`, `if is` or another `keyword`.
Expand Down