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: Allow Irrefutable Patterns in if-let and while-let statements #2086

Merged
merged 11 commits into from
Sep 11, 2017
78 changes: 78 additions & 0 deletions text/0000-allow-if-let-irrefutables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
- Feature Name: allow_if_let_irrefutables
- Start Date: 2017-07-27
- RFC PR:
- Rust Issue:

# Summary
[summary]: #summary

Currently when using an if let statement and an irrefutable pattern (read always match) is used the compiler complains with an `E0162: irrefutable if-let pattern`.
The current state breaks macros who want to accept patterns generically and this RFC proposes changing this error to an error-by-default which is allowed to be disabled by such macros.
Copy link

Choose a reason for hiding this comment

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

error-by-default lint?


# Motivation
[motivation]: #motivation

The use cases for this is in the creation of macros where patterns are allowed because to support the `_` patterns the code has to be rewritten to be both much larger and include a compiler allow.
The expected outcome is for irrefutable patterns to be compiled to a tautology and have the if block accept it as if it was `if true { }`.
To support this, currently you must do something roughly the following, which seems to counteract the benefit of having if-let and while-let in the spec.

```rust
if let $p = $val {
$b
}
```
Cannot be used, so the original match must be. The `allow` is forced so that the warning does not appear to the user of it since `_` won't be matched if `$p` is irrefutable.

```rust
#[allow(unreachable_patterns)]
Copy link
Member

Choose a reason for hiding this comment

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

Question and/or Presentation nit: Is this match $val rust code block in the markdown meant to be associated with the above phrase "currently you must do something roughly the following," (sic) ? Its hard to understand the current presentation, because you say "roughly (like?) the following", but then the immediately following code block is the one using if let.

If my inference is correct in that the "the following" is meant to denote the match $val Rust code block, I would recommend moving the match $val rust code block up above the if let one, and add a little bit of text between the two to make their distinct roles clear.

For example:

To support this, currently you must do something roughly the following:
[[match $val Rust code block here]]
This seems to counteract the benefit of having if-let and while-let in the spec.
The presence of #[allow(unreachable_patterns)] is forced so that the warning does not appear to the user of it since _ won't be matched if $p is irrefutable.

In short, one cannot expand to the following (more desirable) if let based code when $p is irrefutable.
[[if let $p Rust code block here.]]
This is what this RFC is proposing to fix, by making the compiler not as strict.

match $val {
$p => { $b; },
_ => ()
}
```


# Detailed design
[design]: #detailed-design

1. Change the compiler error `irrefutable if-let-pattern` and similar patterns to an `error-by-default` lint that can be disabled by an `#[allow]` statement
2. Proposed lint name: `irrefutable_let_pattern`

Code Example (explicit):
```rust
#[allow(irrefutable_let_pattern)]
Copy link
Contributor

Choose a reason for hiding this comment

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

This example still won't work due to #2086 (comment).

if let _ = 'a' {
println!("Hello World");
}
```

Code Example (implicit):
```rust
macro_rules! check_five {
($p:pat) => {{
#[allow(irrefutable_let_pattern)]
if let $p = 5 {
println!("Pattern matches five");
}
}};
}
```

# How We Teach This
[how-we-teach-this]: #how-we-teach-this

This can be taught by changing the second version of (`The Book`)[https://doc.rust-lang.org/book/second-edition/ch18-02-refutability.html] to not explicitly say that it is not allowed.
Copy link
Member

Choose a reason for hiding this comment

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

you've got [] and () backwards here

Adding that it is a lint that can be disabled.

# Drawbacks
[drawbacks]: #drawbacks

It allows programmers to manually write the line `if let _ = expr { } else { }` which is generally obfuscating and not desirable. However, this will not be explicitly allowed with the `#[allow]`.
Copy link
Member

Choose a reason for hiding this comment

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

You wrote "this will not be explicitly allowed with the #[allow]"

Is this a typo? I.e., I would have expected "this will not be explicitly allowed without the #[allow]"

(Also, I'd probably go ahead and spell out #[allow(irrefutable_let_pattern] here)


# Alternatives
[alternatives]: #alternatives

* The trivial alternative: Do nothing. As your motivation explains, this only matters for macros anyways plus there already is an acceptable workaround (match). Code that needs this frequently can just package this workaround in its own macro and be done.

# Unresolved questions
[unresolved]: #unresolved-questions