首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >模式匹配引用时的奇怪类型

模式匹配引用时的奇怪类型
EN

Stack Overflow用户
提问于 2022-04-04 03:43:29
回答 2查看 211关注 0票数 4

我在阅读这篇文章时遇到了这种奇怪的行为,这篇文章的核心问题是,当你匹配(&k, &v) = &(&String, &String) k k v 时,会得到 String类型。

为了弄清楚发生了什么,我编写了以下测试代码,结果对我来说更令人震惊和困惑:

操场连接

代码语言:javascript
复制
fn main() {
    let x: &(&String, &String) = &(&String::new(), &String::new());
    let ref_to_x: &&(&String, &String) = &x;
    let ref_ref_to_x: &&&(&String, &String) = &&x;
    let ref_ref_ref_to_x: &&&&(&String, &String) = &&&x;
    
    // code snippet 1
    let (a, b) = x;                // type of a: &&String, type of b: &&String
    let (a, b) = ref_to_x;         // type of a: &&String, type of b: &&String
    let (a, b) = ref_ref_to_x;     // type of a: &&String, type of b: &&String
    let (a, b) = ref_ref_ref_to_x; // type of a: &&String, type of b: &&String

    // code snippet 2
    let &(a, b) = x;                // type of a: &String, type of b: &String
    let &(a, b) = ref_to_x;        // type of a: &&String, type of b: &&String
    let &(a, b) = ref_ref_to_x;    // type of a: &&String, type of b: &&String
    let &(a, b) = ref_ref_ref_to_x;// type of a: &&String, type of b: &&String

    // code snippet 3
    let (&a, &b) = x;               // type of a: String, type of b: String
    let (&a, &b) = ref_to_x;        // type of a: String, type of b: String
    let (&a, &b) = ref_ref_to_x;    // type of a: String, type of b: String
    let (&a, &b) = ref_ref_ref_to_x;// type of a: String, type of b: String
}

添加到行尾的ab的类型注释由rust-analyzer推断。

请注意,,由于错误can not move out of xx because it's borrowrd/can not move out of xx which is behind a shared referencecode snippet 3 不会编译,但我认为这并不重要(也许我错了,如果是的话,请指出,谢谢),因为我们正在关注ab的类型。

我的问题是:

  1. 为什么ab总是有相同的类型,即使RHS有不同的类型(x/ref_to_x/ref_ref_to_x.)在代码片段1/2/3中?
  2. 这种匹配是如何发生的(一步一步的匹配过程将被感激)?
  3. 在编写代码时,如何获得与rust-analyzer/rustc完全相同的类型推断?

顺便说一句,这与rfc 2005比赛-人体工程学有关吗?我在谷歌上搜索了很多,发现很多人在他们的回答中提到了这一点。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-04-04 05:28:47

是。你所看到的是符合人体工程学的行动,他们的行为可能不是你所期望的。

匹配人类工效学工作的方法是使用绑定模式。有三种绑定模式,即使没有匹配的人体工程学,也可以使用它们:

  • 移动。在引入match人机工程学之前,这是默认的绑定模式,它总是尝试移动(或复制)值。
  • ref。当您将ref运算符应用于绑定时(令人惊讶),它添加了one引用,这就是您得到的结果。例如,在match e { ref r => ... }中,r&e
  • ref mut。类似于ref,但使用可变的借用(并使用ref mut运算符指定)。

该过程的工作方式如下:编译器从外部向内处理模式。该过程以move作为绑定模式开始。

每次编译器需要将非引用模式(文字、结构、元组、切片)与引用匹配时,它会自动取消引用并更新绑定模式:当&引用匹配时,我们将得到ref绑定模式,而对于&mut引用,如果当前绑定模式为ref或其他ref mut,我们将得到ref。然后这个过程会重复,直到我们没有引用为止。

如果我们要与引用模式(绑定、通配符、引用类型的consts或&/&mut模式)匹配,默认的绑定模式将被重置为move

当一个变量被绑定时,编译器会查看当前的绑定模式:对于move,它将匹配类型为-原样。对于refref mut,它将分别添加&&mut,但只有一个

让我们按照你的例子一条条地走。

代码语言:javascript
复制
let (a, b) = x;                // type of a: &&String, type of b: &&String

我们将非引用模式(元组模式)与引用(类型为&(&String, &String))进行匹配。因此,我们取消引用并将绑定模式设置为ref

现在我们有了一个元组模式来匹配(&String, &String)类型的元组和ref的绑定模式。我们将a&String相匹配:它是一个引用模式(绑定),因此我们不更改绑定模式。但是,我们已经有了ref的绑定模式。我们匹配的类型是&String,而ref意味着我们添加了一个引用,所以我们以&&String结尾。同样的事情也发生在b身上。

代码语言:javascript
复制
let (a, b) = ref_to_x;         // type of a: &&String, type of b: &&String

这里,与前面的示例一样,我们将非引用模式(元组模式)与引用(&&(&String, &String))匹配。因此,我们取消引用并将绑定模式设置为ref。但我们仍然有一个参考资料:&(&String, &String)。所以我们又取消了。绑定模式已经是ref,所以我们不需要触摸它。最后,我们将(a, b)(&String, &String)进行匹配。这意味着a = &Stringb = &String。但是请记住,我们使用的是ref绑定模式,因此我们应该添加一个引用。我们只添加一个引用,,即使我们匹配两个!,在最后,我们有a = &&Stringb = &&String

这个代码片段中的其余示例也是这样工作的。

代码语言:javascript
复制
let &(a, b) = ref_to_x;        // type of a: &&String, type of b: &&String

这里,我们首先将&模式与&&(&String, &String)类型的引用相匹配。这将移除两个引用,导致我们将(a, b)&(&String, &String)相匹配。从现在开始,我们继续,就像在第一个例子。

这个片段中的其余示例也是类似的。

代码语言:javascript
复制
let (&a, &b) = x;               // type of a: String, type of b: String

这是最有趣的一个。还记得我们是如何谈论引用模式还是非参考模式的吗?在这个例子中,这个事实扮演了一个残酷的角色。

首先,我们将元组模式与类型&(&String, &String)相匹配。我们取消对元组的引用并设置binding_mode = ref。现在,我们匹配元组:我们必须将&a&b分别与&String匹配,并将绑定模式设置为ref

当我们将&a&String相匹配时会发生什么?记住,&是一个引用模式,在匹配引用模式时,我们完全忽略了绑定模式。因此,我们将&a&String进行匹配,将绑定模式重置为move。这将从双方删除引用,留给我们a = String&b也一样。

这个代码片段中的下一个示例是相同的。

票数 5
EN

Stack Overflow用户

发布于 2022-04-04 04:23:54

这叫做“毁灭”。它通常用于模式匹配,如与if let Some(val) = option

实质上,这是:

代码语言:javascript
复制
let x: (&String, &String) = (&String::new(), &String::new());
let (&a, &b) = x; // a: String, b: String

相当于:

代码语言:javascript
复制
let (&a, &b) = (&String::new(), &String::new()); // a: String, b: String

这相当于:

代码语言:javascript
复制
let (a, b) = (String::new(), String::new());

为了解释更多,我将使用一个简单的例子。

代码语言:javascript
复制
let value: String = String::new();
let &also_value = &value; // also_value: String

破坏工作是通过“简化”双方来实现的。在这种情况下,它将取消双方的引用,从而导致

代码语言:javascript
复制
let also_value = value;

这也是通过间接进行的。如果我们在中间加上一步:

代码语言:javascript
复制
let value: String = String::new();
let ref_to_value: &String = &value;
let &also_value = ref_to_value; // also_value: String

它可以通过间接看到,现在将取消引用ref_to_value以再次获取String

匹配人机工程学也可能涉及,但只用于取消整个元组。

注意:要获得析构赋值中的引用,可以使用ref

代码语言:javascript
复制
let value: String = String::new();
let ref ref_to_value = value; // ref_to_value: &String

通常,这只用于模式匹配,例如:

代码语言:javascript
复制
if let Some(ref x) = option { ... }
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71731788

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档