Ste*_*Lau 5 types type-inference pattern-matching rust match-ergonomics
我在阅读这篇文章时遇到了这种奇怪的行为,这篇文章的核心问题是当你匹配时(&k, &v) = &(&String, &String),k会v得到类型String。
为了弄清楚发生了什么,我编写了以下测试代码,结果让我更加震惊和困惑:
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
}
Run Code Online (Sandbox Code Playgroud)
我添加到行尾的a和 的类型注释是由 推断出来的。brust-analyzer
请注意,由于错误而code snippet 3 无法编译,但我认为这并不重要(也许我在这里错了,如果是这样,请指出我,谢谢)因为我们专注于和 的can not move out of xx because it's borrowrd/can not move out of xx which is behind a shared reference类型。ab
我的问题是:
a即使bRHS 在代码片段 1/2/3 中具有不同的类型(x/ref_to_x/ref_ref_to_x..),但总是具有相同的类型?rust-analyzer/rustc编写代码时完全相同的类型推断?顺便说一句,这与rfc 2005 比赛人体工程学相关吗?我用谷歌搜索了很多,发现很多人在他们的答案中提到了这一点。
是的。您所看到的是匹配人体工程学的实际效果,但它们的行为可能不是您所期望的。
匹配人体工程学的工作方式是使用绑定模式。共有三种绑定模式可供选择,即使没有符合人体工程学的情况也可以使用:
ref。这就是当您将ref运算符应用于绑定时所得到的结果(令人惊讶),并且它添加了一个引用。例如,在 中match e { ref r => ... },r是&e。ref mut。与 类似ref,但使用可变借用(并使用ref mut运算符指定)。该过程的工作原理如下:编译器从外向内处理模式。move该过程从绑定模式开始。
每次编译器需要将非引用模式(文字、结构、元组、切片)与引用进行匹配时,它都会自动取消引用引用并更新绑定模式:当&引用与引用匹配时,我们将获得ref绑定模式,并且作为&mut参考,我们将获得ref当前绑定模式是否为ref或否则ref mut。然后重复这个过程,直到我们不再有参考为止。
如果我们根据引用模式(绑定、通配符、const引用类型或&/&mut模式)进行匹配,则默认绑定模式将重置回move.
当绑定变量时,编译器会查看当前的绑定模式:对于move,它将按原样匹配类型。对于ref和ref mut,它将分别添加&或&mut。但只有一个。
让我们逐行跟随您的示例。
let (a, b) = x; // type of a: &&String, type of b: &&String
Run Code Online (Sandbox Code Playgroud)
我们将非引用模式(元组模式)与引用(类型为&(&String, &String))进行匹配。所以我们取消引用引用并将绑定模式设置为ref。
现在我们得到了一个元组模式来匹配 type 的元组(&String, &String)和ref. 我们匹配a:&String它是一个参考模式(绑定),所以我们不改变绑定模式。然而,我们已经有了ref. 我们匹配的类型是&String,ref意味着我们添加了一个引用,所以我们以 结尾&&String。完全相同的事情也发生在b。
let (a, b) = ref_to_x; // type of a: &&String, type of b: &&String
Run Code Online (Sandbox Code Playgroud)
在这里,就像前面的示例一样,我们将非引用模式(元组模式)与引用 ( &&(&String, &String)) 进行匹配。因此我们取消引用并将绑定模式设置为ref。但我们还是有一个参考:&(&String, &String)。所以我们再次取消引用。绑定模式已经有了ref,所以我们不需要碰它。(a, b)我们以匹配结束(&String, &String)。这意味着a = &String,b = &String。但请记住我们使用的是ref绑定模式,因此我们应该添加引用。即使我们匹配了两个引用,我们也只添加了一个引用!最后,我们有,。a = &&Stringb = &&String
此代码片段中的其余示例的工作方式相同。
let &(a, b) = ref_to_x; // type of a: &&String, type of b: &&String
Run Code Online (Sandbox Code Playgroud)
在这里,我们首先将&模式与 type 的引用进行匹配&&(&String, &String)。这会删除两个引用,使我们能够(a, b)匹配&(&String, &String). 从现在开始,我们继续像第一个例子一样。
此代码片段中的其余示例类似。
let (&a, &b) = x; // type of a: String, type of b: String
Run Code Online (Sandbox Code Playgroud)
这是最有趣的一个。还记得我们如何讨论参考模式与非参考模式吗?在这个例子中,事实起着至关重要的作用。
首先,我们将元组模式与类型进行匹配&(&String, &String)。我们取消引用 tuple 和 set binding_mode = ref。现在我们匹配元组:我们必须匹配&a和&b每个反对&String,绑定模式设置为ref。
&a当我们匹配时会发生什么&String?好吧,记住&是一个参考模式,当匹配参考模式时我们完全忽略绑定模式。所以我们匹配&a,&String绑定模式重置为move。这会删除双方的引用,留下a = String. 对于 也一样&b。
此代码片段中的下一个示例是相同的。
| 归档时间: |
|
| 查看次数: |
338 次 |
| 最近记录: |