当两个 Options 包含可以被测试是否相等的值时,测试两个 Options 是否相等的最惯用的方法是什么?

What's the most idiomatic way to test two Options for equality when they contain values which can be tested for equality?

我有两种类型可以进行相等性测试。但是,一旦我将它们包装在 Option 中,各种 PartialEq 实现所提供的精确度就完全超过了 window。我必须使用 map 或以其他方式转换它们。

例如,让我们使用 &strString

fn main() {
    let a = "hello";
    let b = "hello".to_owned();

    assert_eq!(a, b); // Just fine

    let a = Some(a);
    let b = Some(b);

    // error: mismatched types
    assert_eq!(a, b);

    // error: mismatched types
    assert_eq!(a, b.as_ref());

    // works, but highly tied to strings or slices,
    // requires me to remember which is which
    assert_eq!(a, b.as_ref().map(|x| &x[..]));
}

肯定有更简单或更直接的方法来做到这一点?

附带问题 — 是什么阻止 Option 更广泛地实施 PartialEq?我在猜测连贯性,我的 老对手

impl<T, U> PartialEq<Option<U>> for Option<T>
where
    T: PartialEq<U>,

RFCs and Rust issues (1, 2] 中对此有一些讨论。

似乎没有技术原因阻止它,看起来只是执行工作的问题。

从 Rust 1.40 开始,您可以使用 as_deref() 这样您就不必记住什么是什么:

assert_eq!(a.as_deref(), b.as_deref());

在 Rust 1.40 之前,我会做这样的事情:

match (&a, &b) {
    (Some(a), Some(b)) => assert_eq!(a, b),
    (None, None) => (),
    _ => panic!("a and b not equal"),
}

另一个选项是自定义断言,基于 assert_eq!:

macro_rules! cmp_eq_option {
    ($left:expr, $right:expr) => {{
        match (&$left, &$right) {
            (Some(left_val), Some(right_val)) => *left_val == *right_val,
            (None, None) => true,
            _ => false,
        }
    }};
}

#[macro_export]
macro_rules! assert_eq_option {
    ($left:expr, $right:expr) => ({
        if !cmp_eq_option!($left, $right) {
            panic!(r#"assertion failed: `(left == right)`
  left: `{:?}`,
 right: `{:?}`"#, $left, $right)
        }
    });
    ($left:expr, $right:expr,) => ({
        assert_eq_option!($left, $right)
    });
    ($left:expr, $right:expr, $($arg:tt)+) => ({
        if !cmp_eq_option!($left, $right) {
            panic!(r#"assertion failed: `(left == right)`
  left: `{:?}`,
 right: `{:?}`: {}"#, $left, $right, format_args!($($arg)+))
        }
    });
}