为什么 Rust 在匹配模式中不执行隐式解引用强制转换?

9
阅读 Rust 书中关于 智能指针和内部可变性 部分后,我尝试作为个人练习编写一个函数,用于遍历智能指针的链表并返回该链表中的“最后”元素。
#[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),
    }
}

考虑到我对隐式解引用和RcDeref特质实现所了解的内容,我本来希望我的第一次尝试能够成功。为什么在这个例子中我必须显式地解引用?

1个回答

10
首先,我们需要了解什么是deref coercion。如果T derefs为Ux是类型为T的值,则:
  • *x*Deref :: deref(& x)
  • &T 可强制转换为&U
  • x.method() 在方法解析期间将检查类型U
方法解析的工作原理是当您在类型上调用方法时,它首先通过添加任何内容到类型,然后添加&,然后添加&mut,然后derefering来检查方法。因此,在确定要调用哪个方法进行x.method()的调用时,它将首先检查接受T&T&mut TU&U&mut U的方法(阅读更多)。这不适用于操作符。因此,==不会强制转换不同类型,这就是为什么必须显式解除引用的原因。
但如果我们使用像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方法。它立即找到了在RcPartialEq实现中定义的一个,签名为fn eq(&self, other: &Rc<List>)。然而,在这种情况下,other的类型是List,无法强制转换为&Rc<List>
那么为什么第二个例子可以工作呢? Nil的类型是List,所以它开始搜索.eq方法。它找不到任何适用于List的方法,因此接下来尝试&List,在那里它找到了派生的PartialEq实现,签名为fn eq(&self, other: &List)。在这种情况下,other的类型是&Rc<List>,由于其Deref实现,可以强制转换为&List。这意味着一切都能正确地通过类型检查,代码可以工作。
至于为什么您的第一次尝试不起作用,似乎这在rust中不是一个功能,有一个提案从2017年开始添加它

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接