Magi c за совпадением: нет реализации для `& std :: option :: Option <ListNode>== std :: option :: Option <_> - PullRequest
2 голосов
/ 13 июля 2020

Я новичок в ie в Rust из Python. Это мой 4-й день изучения Rust.

После моего первого вопроса Приведение типов для типа Option , у меня есть следующий вопрос, связанный с синтаксисом match и концепцией владения.

Во-первых, я объявляю структуру ListNode с реализацией new.

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

impl ListNode {
  #[inline]
  fn new(val: i32) -> Self {
    ListNode {
      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,
    }
}

Это отлично работает, a и b успешно сравниваются с None без * и unwrap_or.

Этот код заканчивается следующими вопросами:

  • Почему * необходимо сравнивать с None в моем исходном коде?
  • Что такое маг c за match? Как этот синтаксис заставляет код пропускать, используя * и unwrap_or для сравнения?

1 Ответ

1 голос
/ 13 июля 2020

Почему * необходимо для сравнения с None в моем исходном коде?

Это не необходимо как таковое, а Eq является реализовано только для идентичных типов, поэтому вам нужно сравнивать либо два Option<T>, либо два &Option<T>. Из этого вы можете увидеть, что сравнение ваших узлов с &None также сработало бы .

Что такое маг c за совпадением? Как этот синтаксис заставляет код пропускать использование * и unwrap_or для сравнения?

Match Ergonomics . По сути, match будет пытаться автоматически добавлять ссылки на шаблоны, чтобы попытаться разрешить типы.

Также я не знаю, заметили ли вы, и просто хотите работать усерднее, но Option<T: Eq> реализует Eq, поэтому is_same(&a, &b) можно было бы просто записать a == b.

И еще, #[inline] обычно не рекомендуется, обычно лучше избегать подсказок, если только вы специально просмотрели сгенерированный вывод, и в противном случае компилятор отказывается предоставить то, что вы ищете. Имейте в виду: код здесь настолько прост, что в режиме выпуска и даже при использовании сложной версии is_same с кучей разыменований llvm узнает, что к чему, и просто загружает и форматирует константу true.

...