Skip to content

Commit d017285

Browse files
author
Cameron Zwarich
committed
RFC: Pattern Guards with Bind-By-Move
1 parent db72aeb commit d017285

File tree

1 file changed

+130
-0
lines changed

1 file changed

+130
-0
lines changed
+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
- Start Date: 2014-06-05
2+
- RFC PR #: (leave this empty)
3+
- Rust Issue #: (leave this empty)
4+
5+
# Summary
6+
7+
Rust currently forbids pattern guards on match arms with move-bound variables.
8+
Allowing them would increase the applicability of pattern guards.
9+
10+
# Motivation
11+
12+
Currently, if you attempt to use guards on a match arm with a move-bound
13+
variable, e.g.
14+
15+
```rust
16+
struct A { a: Box<int> }
17+
18+
fn foo(n: int) {
19+
let x = A { a: box n };
20+
let y = match x {
21+
A { a: v } if *v == 42 => v,
22+
_ => box 0
23+
};
24+
}
25+
```
26+
27+
you get an error:
28+
29+
```
30+
test.rs:6:16: 6:17 error: cannot bind by-move into a pattern guard
31+
test.rs:6 A { a: v } if *v == 42 => v,
32+
^
33+
```
34+
35+
This should be permitted in cases where the guard only accesses the moved value
36+
by reference or copies out of derived paths.
37+
38+
This allows for succinct code with less pattern matching duplication and a
39+
minimum number of copies at runtime. The lack of this feature was encountered by
40+
@kmcallister when developing Servo's new HTML 5 parser.
41+
42+
# Detailed design
43+
44+
This change requires all occurrences of move-bound pattern variables in the
45+
guard to be treated as paths to the values being matched before they are moved,
46+
rather than the moved values themselves. Any moves of matched values into the
47+
bound variables would occur on the control flow edge between the guard and the
48+
arm's expression. There would be no changes to the handling of reference-bound
49+
pattern variables.
50+
51+
The arm would be treated as its own nested scope with respect to borrows, so
52+
that pattern-bound variables would be able to be borrowed and dereferenced
53+
freely in the guard, but these borrows would not be in scope in the arm's
54+
expression. Since the guard dominates the expression and the move into the
55+
pattern-bound variable, moves of either the match's head expression or any
56+
pattern-bound variables in the guard would trigger an error.
57+
58+
The following examples would be accepted:
59+
60+
```rust
61+
struct A { a: Box<int> }
62+
63+
impl A {
64+
fn get(&self) -> int { *self.a }
65+
}
66+
67+
fn foo(n: int) {
68+
let x = A { a: box n };
69+
let y = match x {
70+
A { a: v } if *v == 42 => v,
71+
_ => box 0
72+
};
73+
}
74+
75+
fn bar(n: int) {
76+
let x = A { a: box n };
77+
let y = match x {
78+
A { a: v } if x.get() == 42 => v,
79+
_ => box 0
80+
};
81+
}
82+
83+
fn baz(n: int) {
84+
let x = A { a: box n };
85+
let y = match x {
86+
A { a: v } if *v.clone() == 42 => v,
87+
_ => box 0
88+
};
89+
}
90+
```
91+
92+
This example would be rejected, due to a double move of `v`:
93+
94+
```rust
95+
struct A { a: Box<int> }
96+
97+
fn foo(n: int) {
98+
let x = A { a: box n };
99+
let y = match x {
100+
A { a: v } if { drop(v); true } => v,
101+
_ => box 0
102+
};
103+
}
104+
```
105+
106+
There are issues with mutation of the bound values, but that is true without
107+
the changes proposed by this RFC, e.g.
108+
[Rust issue #14684](https://github.com/mozilla/rust/issues/14684). The general
109+
approach to resolving that issue should also work with these proposed changes.
110+
111+
# Drawbacks
112+
113+
The current error message makes it more clear what the user is doing wrong, but
114+
if this change is made the error message for an invalid use of this feature
115+
(even if it were accidental) would indicate a use of a moved value, which might
116+
be more confusing.
117+
118+
This might be moderately difficult to implement in `rustc`.
119+
120+
# Alternatives
121+
122+
As far as I am aware, the only workarounds for the lack of this feature are to
123+
manually expand the control flow of the guard (which can quickly get messy) or
124+
use unnecessary copies.
125+
126+
# Unresolved questions
127+
128+
This has nontrivial interaction with guards in arbitrary patterns as proposed
129+
in [#99](https://github.com/rust-lang/rfcs/pull/99).
130+

0 commit comments

Comments
 (0)