【问题标题】: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 的了解,我预计我的第一次尝试会奏效。为什么我必须在此示例中显式取消引用?

【问题讨论】:

    标签: rust dereference coercion


    【解决方案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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-08
      • 1970-01-01
      • 2011-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-10
      相关资源
      最近更新 更多