Как утверждать, что две переменные равны, когда реализация PartialEq не подходит? - PullRequest
1 голос
/ 05 мая 2020

В моей программе я представляю «события» следующей структурой:

struct Event {
    value: &'static str,
    timestamp: usize,
}

До сих пор я использовал PartialEq для сравнения Event переменных: в большинстве случаев я рассматриваю две Event s должны быть равны, если их value одинаковы:

impl PartialEq for Event {
    fn eq(&self, other: &Self) -> bool {
        self.value == other.value
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_loose_equality() {
        let a = Event { value: "a-value", timestamp: 12345 };
        let b = Event { value: "a-value", timestamp: 23456 };

        assert_eq!(a, b);
    }
}

Однако в некоторых тестах я хотел бы убедиться, что две такие переменные «строго равны»: тест не должен имеют разные timestamp (не совпадают по отношению к Eq).

Согласно документации assert_eq!:

Утверждает, что два выражения равны каждому другое (с использованием PartialEq). источник

Итак, я ищу Eq эквивалент, assert_Eq_eq! в сортировке.

(или я не понимаю, как Eq работает и должен использоваться?)

Вот что я не могу выполнить:

impl Eq for Event {}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_strict_equality() {
        let a = Event { value: "a-value", timestamp: 12345 };
        let b = Event { value: "a-value", timestamp: 12345 };

        // ???
    }
}

Ответы [ 5 ]

3 голосов
/ 05 мая 2020

Вы плывете вверх по течению, борясь против течения. Go с потоком. Пусть PartialEq будет строгим равенством и определит отдельную черту или метод для свободного равенства.

#[derive(Eq, PartialEq)]
struct Event { .. }

impl Event {
    fn loose_eq(&self, other: &Self) -> bool {
        self.value == other.value
    }
}
#[test]
fn test_loose_equality() {
    let a = Event { value: "a-value", timestamp: 12345 };
    let b = Event { value: "a-value", timestamp: 23456 };

    assert!(a.loose_eq(b));
}
3 голосов
/ 05 мая 2020

Я бы создал «представления» соответствующего типа:

struct Event {
    value: &'static str,
    timestamp: usize,
}

#[derive(Debug, PartialEq)]
struct Exact<'a> {
    value: &'a &'static str,
    timestamp: &'a usize,
}

impl Event {
    fn exact(&self) -> Exact<'_> {
        let Self { value, timestamp } = self;
        Exact { value, timestamp }
    }
}

fn demo(a: Event, b: Event) {
    assert_eq!(a.exact(), b.exact());
}

Здесь я решил взять ссылки на каждое из полей, чтобы продемонстрировать общий случай, но вам не нужны ссылки для этого конкретного примера c (&str и usize реализуют Copy и имеют небольшой размер).

Вы также можете вообще не реализовывать PartialEq на исходном типе и только выполнять сравнение через просмотры:

assert_eq!(a.exact(), b.exact());
assert_eq!(a.loose(), b.loose());
2 голосов
/ 05 мая 2020

Если вам нужно строгое равенство только в тестах, а ваша структура имеет только два поля, я бы просто сравнил эти поля напрямую:

let a = Event { value: "a-value", timestamp: 12345 };
let b = Event { value: "a-value", timestamp: 12345 };
assert_eq!(a.value, b.value);
assert_eq!(a.timestamp, b.timestamp);

Для меня это самый простой и самый читаемый вариант .

1 голос
/ 05 мая 2020

Да, вы чего-то не понимаете. Eq не имеет новых методов, кроме PartialEq. Это просто утверждение, что реализация PartialEq рефлексивна (а также транзитивность и симметрия, предполагаемые с помощью PartialEq).

Если вы хотите иметь другое «строгое» равенство, вы можете сделать свой собственная черта (если вы планируете использовать это много) или просто иметь метод fn strict_eq(&self, other: &Self) -> bool, прикрепленный к Event.

Имея такой метод, вы могли бы написать макрос для использования этого метода.

Полный исходный код assert_eq! можно найти здесь , и его можно легко адаптировать, но (возможно, чрезмерно) простая версия будет выглядеть примерно так:

macro_rules! assert_strict_eq {
    ($left: expr, $right: expr) => {
        if !$left.strict_eq(&$right) {
            panic!(r#"assertion failed: `(left == right)`
  left: `{:?}`,
 right: `{:?}`"#, $left, $right)
        }
    }
}

( пример использования)

0 голосов
/ 05 мая 2020

В отличие от JavaScript, Rust не поддерживает равенство и строгое равенство.

Однако вы можете добиться аналогичного эффекта, реализуя свой собственный трейт и макрос:

pub trait StrictEq {
    fn strict_eq(&self, other: &Self) -> bool;
}

macro_rules! assert_strict_eq {
    ($left:expr, $right:expr) => {{
        match (&$left, &$right) {
            (left_val, right_val) => {
                assert!(left_val.strict_eq(right_val));
            }
        }
    }};
}

Реализация для Event будет довольно просто:

impl StrictEq for Event {
    fn strict_eq(&self, other: &Self) -> bool {
        self.value == other.value && self.timestamp == other.timestamp
    }
}
...