为什么 Rust 不在匹配模式中执行隐式取消引用强制?
Why does Rust not perform implicit deref coercion in match patterns?
阅读 Rust 书中关于 Smart Pointers and Interior mutability 的部分后,作为个人练习,我尝试编写一个函数来遍历智能指针的链表和 return “最后一个” " 列表中的元素:
#[derive(Debug, PartialEq)]
enum List {
Cons(Rc<RefCell<i32>>, Rc<List>),
Nil,
}
use crate::List::{Cons, Nil};
fn get_last(list: &List) -> &List {
match list {
Nil | Cons(_, Nil) => list,
Cons(_, next_list) => get_last(next_list),
}
}
此代码导致以下错误:
| Nil | Cons(_, Nil) => list,
| ^^^ expected struct `std::rc::Rc`, found enum `List
我能够通过使用“匹配守卫”并在 Cons(_, x)
模式上明确取消引用来让它工作:
fn get_last(list: &List) -> &List {
match list {
Nil => list,
Cons(_, next_list) if **next_list == Nil => list,
Cons(_, next_list) => get_last(next_list),
}
}
鉴于我对隐式取消引用和 Rc
的 Deref
特性实现的了解,我本以为我的第一次尝试会成功。为什么我必须在此示例中显式取消引用?
首先,我们需要了解什么是解引用强制。如果 T
解引用到 U
并且 x
是类型 T
的值,那么:
*x
是 *Deref::deref(&x)
&T
可以强制转换为 &U
x.method()
将在方法解析期间检查类型 U
。
方法解析的工作原理是,当您在类型上调用方法时,它首先通过不向类型添加任何内容来检查方法,然后添加 &
,然后添加 &mut
,然后取消引用.因此,在确定为 x.method()
调用哪个方法时,它将首先检查采用 T
的方法,然后是 &T
,然后是 &mut T
,然后是 [=13] =],然后是 &U
,然后是 &mut U
(read more here)。 不适用于运算符。因此,==
不会强制转换不同的类型,这就是为什么您必须显式取消引用的原因。
但是如果我们确实在 PartialEq
特征中使用了一个方法,比如 .eq
呢?事情变得有趣了。以下代码失败:
fn get_last(list: &List) -> &List {
match list {
Nil => list,
Cons(_, next_list) if next_list.eq(Nil) => list,
Cons(_, next_list) => get_last(next_list),
}
}
但以下成功:
fn get_last(list: &List) -> &List {
match list {
Nil => list,
// notice how it's Nil.eq and not next_list.eq
Cons(_, next_list) if Nil.eq(next_list) => list,
Cons(_, next_list) => get_last(next_list),
}
}
这是为什么?让我们看第一个例子:
next_list
是 &Rc<List>
类型,因此它开始搜索 .eq
方法。它立即找到一个在 PartialEq
实现中为 Rc
定义的签名 fn eq(&self, other: &Rc<List>)
。但是,other
在这种情况下属于 List
类型,无法将其强制转换为 &Rc<List>
.
那为什么第二个有效呢?
Nil
属于 List
类型,因此它开始搜索 .eq
方法。它找不到 List
的任何内容,因此它接下来尝试 &List
,在那里它找到带有签名 fn eq(&self, other: &List)
的派生 PartialEq
实现。在这种情况下,other 是 &Rc<List>
类型,由于它的 Deref
实现,它可以被强制转换为 &List
。这意味着所有内容都可以正确进行类型检查并且代码可以正常工作。
至于为什么你的第一次尝试没有成功,这似乎不是 Rust 的一个特性,而是 a proposal to add it dating back to 2017。
阅读 Rust 书中关于 Smart Pointers and Interior mutability 的部分后,作为个人练习,我尝试编写一个函数来遍历智能指针的链表和 return “最后一个” " 列表中的元素:
#[derive(Debug, PartialEq)]
enum List {
Cons(Rc<RefCell<i32>>, Rc<List>),
Nil,
}
use crate::List::{Cons, Nil};
fn get_last(list: &List) -> &List {
match list {
Nil | Cons(_, Nil) => list,
Cons(_, next_list) => get_last(next_list),
}
}
此代码导致以下错误:
| Nil | Cons(_, Nil) => list,
| ^^^ expected struct `std::rc::Rc`, found enum `List
我能够通过使用“匹配守卫”并在 Cons(_, x)
模式上明确取消引用来让它工作:
fn get_last(list: &List) -> &List {
match list {
Nil => list,
Cons(_, next_list) if **next_list == Nil => list,
Cons(_, next_list) => get_last(next_list),
}
}
鉴于我对隐式取消引用和 Rc
的 Deref
特性实现的了解,我本以为我的第一次尝试会成功。为什么我必须在此示例中显式取消引用?
首先,我们需要了解什么是解引用强制。如果 T
解引用到 U
并且 x
是类型 T
的值,那么:
*x
是*Deref::deref(&x)
&T
可以强制转换为&U
x.method()
将在方法解析期间检查类型U
。
方法解析的工作原理是,当您在类型上调用方法时,它首先通过不向类型添加任何内容来检查方法,然后添加 &
,然后添加 &mut
,然后取消引用.因此,在确定为 x.method()
调用哪个方法时,它将首先检查采用 T
的方法,然后是 &T
,然后是 &mut T
,然后是 [=13] =],然后是 &U
,然后是 &mut U
(read more here)。 不适用于运算符。因此,==
不会强制转换不同的类型,这就是为什么您必须显式取消引用的原因。
但是如果我们确实在 PartialEq
特征中使用了一个方法,比如 .eq
呢?事情变得有趣了。以下代码失败:
fn get_last(list: &List) -> &List {
match list {
Nil => list,
Cons(_, next_list) if next_list.eq(Nil) => list,
Cons(_, next_list) => get_last(next_list),
}
}
但以下成功:
fn get_last(list: &List) -> &List {
match list {
Nil => list,
// notice how it's Nil.eq and not next_list.eq
Cons(_, next_list) if Nil.eq(next_list) => list,
Cons(_, next_list) => get_last(next_list),
}
}
这是为什么?让我们看第一个例子:
next_list
是 &Rc<List>
类型,因此它开始搜索 .eq
方法。它立即找到一个在 PartialEq
实现中为 Rc
定义的签名 fn eq(&self, other: &Rc<List>)
。但是,other
在这种情况下属于 List
类型,无法将其强制转换为 &Rc<List>
.
那为什么第二个有效呢?
Nil
属于 List
类型,因此它开始搜索 .eq
方法。它找不到 List
的任何内容,因此它接下来尝试 &List
,在那里它找到带有签名 fn eq(&self, other: &List)
的派生 PartialEq
实现。在这种情况下,other 是 &Rc<List>
类型,由于它的 Deref
实现,它可以被强制转换为 &List
。这意味着所有内容都可以正确进行类型检查并且代码可以正常工作。
至于为什么你的第一次尝试没有成功,这似乎不是 Rust 的一个特性,而是 a proposal to add it dating back to 2017。