Skip to content

Commit 7fa7307

Browse files
committed
First draft of if-not-let RFC
1 parent 8e2d3a3 commit 7fa7307

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed

text/0000-if-not-let.md

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
- Feature Name: if-not-let expression
2+
- Start Date: 2015-09-30
3+
- RFC PR:
4+
- Rust Issue:
5+
6+
# Summary
7+
8+
Introduce a new `if !let PAT = EXPR { BODY }` construct (informally called an
9+
**if-not-let expression**). This works much like an if-let expression, but
10+
executes its body when pattern matching fails.
11+
12+
This narrows the gap between regular `if` expressions and `if let`
13+
expressions, while also providing a simpler syntax for some common
14+
error-handling patterns.
15+
16+
# Motivation
17+
18+
[if-let expressions][if-let] offer a succinct syntax for pattern matching
19+
with only one "success" path. This is particularly useful for unwrapping
20+
types like `Option`. However, an if-let expression can only create bindings
21+
within its body, which can force rightward drift and excessive nesting.
22+
23+
`if !let` is a logical extension of `if let` that moves the failure case into
24+
the body, and allows the success case to follow without extra nesting.
25+
26+
For example, this code written with current Rust syntax:
27+
28+
```rust
29+
if let Some(a) = x {
30+
if let Some(b) = y {
31+
if let Some(c) = z {
32+
/*
33+
* do something with a, b, and c
34+
*/
35+
} else {
36+
return Err("bad z");
37+
}
38+
} else {
39+
return Err("bad y");
40+
}
41+
} else {
42+
return Err("bad x");
43+
}
44+
```
45+
46+
would become:
47+
48+
```rust
49+
if !let Some(a) = x {
50+
return Err("bad x");
51+
}
52+
if !let Some(b) = y {
53+
return Err("bad y");
54+
}
55+
if !let Some(c) = z {
56+
return Err("bad z");
57+
}
58+
/*
59+
* do something with a, b, and c
60+
*/
61+
```
62+
63+
It's possible to use `match` statements to emulate this today, but at a
64+
significant cost in length and readability. For example, this real-world code
65+
from Servo:
66+
67+
```rust
68+
let subpage_layer_info = match layer_properties.subpage_layer_info {
69+
Some(ref subpage_layer_info) => *subpage_layer_info,
70+
None => return,
71+
};
72+
```
73+
74+
is equivalent to this much simpler if-not-let expression:
75+
76+
```rust
77+
if !let Some(subpage_layer_info) = match layer_properties.subpage_layer_info {
78+
return
79+
}
80+
```
81+
82+
The Swift programming language, which inspired Rust's if-let expression, also
83+
includes [guard-let-else][swift] expressions which are equivalent to this
84+
proposal except for the choice of keywords.
85+
86+
# Detailed design
87+
88+
Extend the Rust expression grammar to include the following production:
89+
90+
```
91+
expr_if_not_let = 'if' '!' 'let' pat '=' expr block
92+
```
93+
94+
The pattern must be refutable. The body of the if-not-let expression (the
95+
`block`) is evaluated only if the pattern match fails. Any bindings created
96+
by the pattern match will be in scope after the if-not-let expression (but not
97+
within its body).
98+
99+
The body must diverge (i.e., it must panic, loop infinitely, call a diverging
100+
function, or transfer control out of the enclosing block with a statement such
101+
as `return`, `break`, or `continue`). Therefore, code immediately following
102+
the if-not-let expression is evaluated only if the pattern match succeeds.
103+
104+
An if-not-let expression has no `else` clause, because it is not needed.
105+
(Instead of an `else` clause, code can simply be placed after the expression.)
106+
107+
The type of an if-not-let expression is `()`.
108+
109+
The following code:
110+
111+
```rust
112+
{
113+
if !let pattern = expression {
114+
/* handle error */
115+
}
116+
/* do something with `pattern` here */
117+
}
118+
```
119+
120+
is equivalent to this code in current Rust:
121+
122+
```rust
123+
match expression {
124+
pattern => {
125+
/* do something with `pattern` here */
126+
}
127+
_ => {
128+
/* handle error */
129+
}
130+
}
131+
```
132+
133+
134+
# Drawbacks
135+
136+
* Allowing a block expression to create bindings that live outside of its body
137+
may be surprising.
138+
139+
* `if !let` is not very visually distinct from `if let` due to the
140+
similarities between the `!` and `l` glyphs.
141+
142+
# Alternatives
143+
144+
* Don't make any changes; use existing syntax like `if let` and `match` as
145+
shown above, or write macros to simplify the code.
146+
147+
* Consider alternate syntaxes for this feature, perhaps closer to Swift's `guard
148+
let else`.
149+
150+
# Unresolved questions
151+
152+
[if-let]: https://github.com/rust-lang/rfcs/blob/master/text/0160-if-let.md
153+
[swift]: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-ID525

0 commit comments

Comments
 (0)