匹配背后的魔法:`&std::option::Option<ListNode> == std::option::Option<_> 没有实现

Magic behind match: no implementation for `&std::option::Option<ListNode> == std::option::Option<_>

我是 Python 的 Rust 新手。这是我学习 Rust 的第 4 天。

在我的第一个问题 之后,我有一个与语法 match 和所有权概念相关的后续问题。


首先,我声明一个 ListNode 结构 new 实现。

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ListNode {
  pub val: i32,
}

impl ListNode {
  #[inline]
  fn new(val: i32) -> Self {
    ListNode {
      val
    }
  }
}

我的目标是通过比较节点的val来比较两个节点是否相同。这是我丑陋的实现。

fn is_same(a: &Option<ListNode>, b: &Option<ListNode>) -> bool {
    if a == None && b == None { return true; }
    else if a == None && b != None { return false; }
    else if a != None && b == None { return false; }

    let ca: ListNode = a.clone().unwrap_or(ListNode::new(0));
    let cb: ListNode = b.clone().unwrap_or(ListNode::new(0));
    if ca.val == cb.val { return true; }
    else { return false; }
}

fn main() {
    let a: Option<ListNode> = Some(ListNode::new(0));
    let b: Option<ListNode> = Some(ListNode::new(0));
    
    println!("{:?}", is_same(&a, &b));
}

然后,我得到了很多错误...

no implementation for `&std::option::Option<ListNode> == std::option::Option<_>

根据我对所有权概念的了解,使用 * 作为借用参数应该是可选的。但是,为了进行比较,我需要添加 *。下面修改后的功能有效。 (另一个发现是,使用 a.clone() 没问题,但 *a.clone() 是错误的,错误为 type `ListNode` cannot be dereferenced。)

fn is_same(a: &Option<ListNode>, b: &Option<ListNode>) -> bool {
    if *a == None && *b == None { return true; }
    else if *a == None && *b != None { return false; }
    else if *a != None && *b == None { return false; }

    let ca: ListNode = a.clone().unwrap_or(ListNode::new(0));
    let cb: ListNode = b.clone().unwrap_or(ListNode::new(0));
    if ca.val == cb.val { return true; }
    else { return false; }
}

由于上面的解决方案代码太难看,这里是另一个使用match的实现,没有多余的unwrap_or*.

fn is_same(a: &Option<ListNode>, b: &Option<ListNode>) -> bool {
    match (a, b) {
        (None, None) => true,
        (None, _) => false,
        (_, None) => false,
        (Some(a), Some(b)) => a.val == b.val,
    }
}

这很完美,ab 与没有 *unwrap_orNone 成功比较。


这些代码结束于以下问题:

Why * is needed to compare with None in my original code?

本身不需要,但Eq仅针对相同类型实现,因此您需要比较两个Option<T>或两个&Option<T>。从这里你可能会看到 comparing your nodes to &None would also have worked.

What is the magic behind match? How does this syntax make code skip using * and unwrap_or for comparison?

Match Ergonomics。基本上,match 将尝试自动添加对模式的引用以尝试解析类型。

另外不知道大家有没有注意到,只是想更加努力,但是Option<T: Eq>实现了Eq,所以is_same(&a, &b)could just be written a == b.

还有一件事,通常建议不要使用 #[inline],通常最好避免提供提示,除非您专门查看了生成的输出并且编译器拒绝提供您正在寻找的内容.请记住:这里的代码非常简单,以至于在发布模式下,甚至使用带有一堆取消引用的复杂版本 is_same,llvm 都会找出什么是什么,然后加载和格式化常量 true