Hi, rustaceans, I have some trouble understanding match ergonomics.
Let me give you some background info. Some time ago, I asked this question on stack overflow. After reading the accepted answer, I thought I have understood what it is , and what will the
manipulation algorithm do. Yesterday, I reviewed this question, finding some contradictions.
Here is the algorithm in English. Original answer quoted:
The process works as follows: the compiler process the pattern from the outside inwards. The process starts with
move
as the binding mode.Each time the compiler needs to match a non-reference pattern (literal, struct, tuple, slice) against a reference, it automatically dereferences the reference and updates the binding mode: when
&
reference is matched against we'll get theref
binding mode, and for&mut
references we will getref
if the current binding mode isref
or otherwiseref mut
. Then this process repeats until we don't have a reference anymore.If we're matching against a reference pattern (binding, wildcard,
const
s of reference types or&
/&mut
patterns), the default binding mode is reset back tomove
.
According to this description, I wrote a pseudo code to demo the algorithm:
1 let mut binding_mode = `move`; // the default binding mode is `move`
2
3 loop (until we don't have a reference to match anymore) {
4 if (encounters `non-ref pattern`) {
5 deref ref; // dereference the reference we are matching
6
7 if (the reference we are matching is `&xx`) {
8 binding_mode = `ref`
9 } else if (the reference we are matching is `&mut xx`) {
10 if (binding_mode == `ref`) {
11 binding_mode = `ref`;
12 } else {
13 binding_mode = `ref mut`;
14 }
15 }
16 } else if (encounters `ref pattern`) {
17 binding_mode = `move`;
18 }
19 }
20
21 // when loop is done:
21 match binding_mode {
22 move => directly bind,
23 ref => bind as ref,
24 ref mut => bind as ref mut,
25 }
And I use this algorithm to test the following cases:
// case 1
let (a, b) = &(&String, &String); // a and b are of type &&String
// case 2
let (&a, &b) = &(&String, &String); // a and b are of type String
Let me quote the original answerer's inference:
Inference for case 2:
This is the most interesting one. Remember how we talked about reference vs. non-reference patterns? In this example that fact plays a crucial role.
First we match the tuple pattern against the type
&(&String, &String)
. We dereference the tuple and setbinding_mode = ref
. Now we match the tuple: we got to match&a
and&b
each against&String
, with the binding mode set toref
.What happens when we match
&a
against&String
? [BREAK POINT 1]Well, remember
&
is a reference pattern, and when matching reference patterns we completely ignore the binding mode(aka. set binding mode tomove
). So we match&a
against&String
, with the binding mode reset tomove
. This removes the reference from both sides, leaving us witha = String
. Same for&b
.
Inference for case 1:
We match a non-reference pattern (a tuple pattern) against a reference (of type
&(&String, &String)
). So we dereference the reference and set the binding mode toref
.Now we got a tuple pattern to match against a tuple of type
(&String, &String)
and a binding mode ofref
. We matcha
against&String
: [BREAK POINT 2]it's a reference pattern (binding), and so we don't change the binding mode. However, we already have a binding mode of
ref
. The type we match against is&String
, andref
means we add a reference, so we end with&&String
. Exactly the same thing happens tob
.
My questions are:
-
What should we do when encountering a reference pattern?
At [BREAK POINT 1] and [BREAK POINT 2], we are both encounting
reference pattern
, but in case 2,we set binding mode to the default move. And in case 1, we didn't change the binding mode
-
None of the inferences above are consistent with the algorithm. The
loop
shouldn't stop until there is no more references to match, the inference for case 2 ends the whole inference when encountering reference pattern.// what inference for case 2 is somthing like this. else if (encounters `ref pattern`) { binding_mode = `move` // Is this right? directly bind; exit(0); }
And in the inference for case 1, at [BREAK POINT 2], encountering reference pattern does not
reset binding mode back to move
. So it seems that the algorithm is incorrect and incomplete.What is the right and deterministic algorithm for this inference process?