为啥 Rust 不在匹配模式中执行隐式取消引用强制?

Posted

技术标签:

【中文标题】为啥 Rust 不在匹配模式中执行隐式取消引用强制?【英文标题】:Why does Rust not perform implicit deref coercion in match patterns?为什么 Rust 不在匹配模式中执行隐式取消引用强制? 【发布时间】:2020-11-01 04:41:16 【问题描述】:

阅读 Rust 书中Smart Pointers and Interior mutability 上的部分后,作为个人练习,我尝试编写一个函数,该函数将遍历智能指针的链表并返回列表中的“最后一个”元素:

#[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),
    

鉴于我对隐式取消引用和Deref 特征实现Rc 的了解,我预计我的第一次尝试会奏效。为什么我必须在此示例中显式取消引用?

【问题讨论】:

【参考方案1】:

首先,我们需要了解 deref coercion 是什么。如果T 引用U 并且xT 类型的值,则:

*x*Deref::deref(&amp;x) &amp;T 可以强制转换为 &amp;U x.method() 将在方法解析期间检查类型 U

方法解析的工作原理是,当您在类型上调用方法时,它首先通过向类型中添加任何内容来检查方法,然后添加&amp;,然后添加&amp;mut,然后解除引用。因此,在确定调用 x.method() 的方法时,它会首先检查一个使用 T 的方法,然后是 &amp;T,然后是 &amp;mut T,然后是 U,然后是 &amp;U,然后是 @ 987654341@ (read more here)。这适用于运营商。因此,== 不会强制转换不同的类型,这就是您必须显式取消引用的原因。

但是如果我们确实在PartialEq trait 中使用了.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&amp;Rc&lt;List&gt; 类型,因此它开始搜索.eq 方法。它立即找到在RcPartialEq 实现中定义的签名fn eq(&amp;self, other: &amp;Rc&lt;List&gt;)。但是,other 在这种情况下属于List 类型,不能强制转换为&amp;Rc&lt;List&gt;

那么为什么第二个工作? NilList 类型,因此它开始搜索 .eq 方法。它找不到List 的任何内容,因此它接下来尝试&amp;List,在那里它找到了带有签名fn eq(&amp;self, other: &amp;List) 的派生PartialEq 实现。在这种情况下, other 是&amp;Rc&lt;List&gt; 类型,由于其Deref 实现,它可以被强制转换为&amp;List。这意味着所有的类型检查都正确并且代码可以正常工作。

至于为什么你的第一次尝试没有成功,它似乎不是 rust 的一个功能,还有a proposal to add it dating back to 2017。

【讨论】:

以上是关于为啥 Rust 不在匹配模式中执行隐式取消引用强制?的主要内容,如果未能解决你的问题,请参考以下文章

为啥打印指针与打印取消引用的指针打印相同的东西?

为啥不在空的 Spark 集群上强制执行preferredLocations?

JS强制类型转换,隐式类型转换, == 和===的区别

在 rust 中引用和取消引用数据库连接

在 Rust 中取消引用字符串和 HashMap

为啥在某些 trait 方法调用中会出现来自 &mut 的引用减弱?