匹配人体工学遇到参考模式会怎么办?

What will match ergonomics do when encounters reference pattern?

看过the rfc 2005,知道操纵的过程是一个反复的操作。我说 encounters reference pattern,我不是在谈论在第一次迭代时遇到参考模式,如下所示:

let x = &String;
// The binding mode is `move`, so x is of type `&String`

但有些情况下 binding mode 在之前的 non-reference pattern 迭代中移动到 ref/ref mut,然后遇到 reference pattern.

我仔细阅读了rfc,发现了这句话The *default binding mode* only changes when matching a reference with a non-reference pattern.,这是否意味着reference pattern不会对匹配过程做任何事情,但是有人告诉我reference pattern会将绑定模式重置为默认值 move(参见案例 2)。

有两种情况“正确”的推论似乎相互冲突:

Correct means that the inferred type is the same as the one inferred by the compiler, but I don't know if the inferring process is also correct, which is the reason why I post this question.

// 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

案例 1 的推论:

首先,我们将 &(&String, &String)(a, b) 进行匹配,而 (a, b)non-reference pattern,因此我们取消引用 &(&String, &String) 并设置 binding modemove(default one)ref。然后我们将 (&String, &String)(a, b) 匹配,使用 ref 绑定模式, ab&&String.

类型

Why did it stop at matching (&String, &String) against (a, b), shouldn't we continue to match &String against a and b respectively?

If we continue, matching &String against a, a is reference pattern, what should we do now?

案例 2 的推论:

首先,我们将 &(&String, &String)(&a, &b) 进行匹配,(&a, &b)non-reference pattern,因此我们取消引用 &(&String, &String) 并将 binding mode 设置为ref。然后我们匹配(&String, &String)(&a, &b),这基本上是匹配&String&a&areference pattern,所以我们将绑定模式重置为 move,使 a 成为 String.

类型

矛盾:

情况2,当遇到引用模式时,我们所做的是将绑定模式重置为默认move。但是在情况 1 中(如果我们没有在那一点停止,保持 &Stringa 匹配),a 也是 reference pattern,如果我们仍然将绑定模式重置为移动,那么 a 的类型将是 &String 而不是 &&String.

还有一个问题,就是when should this inferring algorithm stop?在情况 1 中,如果算法 应该 在该点停止,那么一切都有意义。

看来我找到了答案。有几件事需要澄清:

  1. match ergonomics只涵盖了我们匹配non-reference patternreference的情况,所以这个问题的标题是错误的,match ergonomics遇到reference pattern什么都没有。 match ergonomics 的算法是这样的:

    fn match_ergonomics() {
        if (the reference we are matching is `&xx`) {
            binding_mode = `ref`
        } else if (the reference we are matching is `&mut xx`) {
            if (binding_mode == `ref`) {
                binding_mode = `ref`;
            } else {
                binding_mode = `ref mut`;
            }
        }
    }
    
  2. default binding mode是为match ergonomics引入的概念,但这并不是match ergonomics涵盖的情况所独有的,它已经融入了整个rust匹配系统。

  3. reference pattern 的概念可能会造成混淆。在 rfc:

    A reference pattern is any pattern which can match a reference without coercion. Reference patterns include bindings, wildcards (_), consts of reference types, and patterns beginning with & or &mut.

    而在 Rust 参考中,它只是以 &/&mut 开头的模式。

  4. 匹配是一个递归的过程,对于这个递归的每一个过程,根据我们遇到的pattern有几种情况,其中包括match ergonomics种情况

  5. 其实有两种情况我们会改变DBD,一种是匹配non-reference patternreference,另一种是匹配&[mut] patreference.

    matching against change to DBM
    non-ref pattern reference set to ref or ref mut
    &[mut] pat reference set to move

匹配算法

// The terminologies we are using in this algorithm come from rust reference
If no `default binding mode` is set, set the `default binding mode` to `move`.

If the pattern is an identifier pattern:
    If we have explicit `ref`:
        bind by reference;
    else if we have explicit `ref mut`:
        bind by mutable reference;
    else:
        // When explicit `ref/ref mut` are absent
        // default binding mode decides how to bind
        If the binding mode is `move`, bind directly.
        If the binding mode is `ref`, bind by immutable reference.
        If the binding mode is `ret mut`, bind by mutable reference.
    
    If the pattern has an inner pattern, repeat this process with that pattern.

else if the pattern is a `&[mut]` reference pattern:
    // matching `&[mut]` against non-reference triggers
    // a compiler error
    Ensure that we are matching against a `&[mut]` reference.

    Dereference the scrutinee expression.
    Set the binding mode to `move`.  
    Repeat this process with the inner pattern.

else if the pattern is any kind of destructuring pattern:
    Set `T` to the type implied by the pattern.
    Ensure that we are matching against a `T` value or `&[mut] T` reference.
    
    // cases covered by match ergonomics
    If we are matching against a `&T` reference:
        Dereference the scrutinee expression.
        Set the binding mode to `ref`.
    else if we are matching against a `&mut T` reference:
        Dereference the scrutinee expression.
        If the binding mode is `move`, set the binding mode to `ref mut`.


    else
        destructure the value;
    Repeat this process for all fields in the pattern.

案例 1 和案例 2 的推断

// case 1
let (a, b) = &(&String, &String); // a and b are of type &&String

第一个过程:我们将 (a, b)&(&String, &String) 进行匹配,这进入了案例 3​​(任何其他类型的解构模式)。 &(&String, &String) 是一个 &T,因此取消引用并将 default binding mode 设置为 ref

第二步:我们正在匹配(a, b)(&String, &String),这基本上是匹配a&Stringa 是一个标识符模式,它进入案例 1。我们没有明确的 ref/ref mut 并且 default binding moderef,所以通过不可变引用绑定,使 a 类型 &&String.

所有模式都匹配,递归结束。

// case 2
let (&a, &b) = &(&String, &String); // a and b are of type String

第一个过程:我们将 (&a, &b)&(&String, &String) 进行匹配,这进入了案例 3​​。取消引用并将 default binding mode 设置为 ref

第二步:我们正在匹配(&a, &b)(&String, &String),这基本上是匹配&a&String&String 是一个 reference pattern,所以我们进入情况 2,并将 default binding mode 设置为 move&a = &String,因此 a 的类型为 String

所有模式都匹配,递归结束。

算法可能不是那么准确,欢迎懂这方面的人来编辑这个答案。