for ... .zip() 之后的 .next():防止迭代器被移动

bri*_*n_o 3 rust

我是 Rust 新手,还没有完全理解它的全部内容。我做了很多 Rust 阅读,也解决了数学问题。我用这个签名编写了一个函数:

pub fn prime_factors(n: u64) -> Vec<u64> {
Run Code Online (Sandbox Code Playgroud)

因此,给定n30,它应该返回一个值为 2、3 和 5 的向量。我构建了一个测试,内容如下:

#[test]
fn prime_factors_test() {
    assert_list_eq([2,3,5].iter(), prime_factors(30).iter());
}
Run Code Online (Sandbox Code Playgroud)

一个亮点是我正在将静态数组与向量进行比较(出于学习目的,这目前是可取的,因为我想练习泛型)。

我的测试功能是我实际上遇到问题的功能。要进行测试,该函数必须迭代两个集合,检查每个索引是否相等。我曾经.zip()这样做过,但如果集合不均匀,迭代器只会通过较短集合的索引进行耗尽。所以之后zip,我想检查两个迭代器是否有额外的元素。

以下内容无法编译:

fn assert_list_eq<I, T>(mut expected: I, mut actual: I)
    where I: Iterator<Item=T> + Clone,
          T: PartialEq + Debug {
    for (e, a) in expected.zip(actual) {
        assert_eq!(e, a);
    }
    // fail if there are any "leftovers"
    assert_eq!(None, expected.next());
    assert_eq!(None, actual.next());
}
Run Code Online (Sandbox Code Playgroud)

我认为循环(或者可能是循环的设置)移动/消耗迭代器,并且它们在循环之后不能使用。但我检查循环后这些迭代器的状态。我想我知道出了什么问题,只是不知道如何改正。我几乎肯定遇到了概念问题。

作为一种解决方法,我这样做了,但我觉得我正在拥抱一种反模式,通过使用.clone()just 让事情正常进行。

fn assert_list_eq<I, T>(expected: I, actual: I)
    where I: Iterator<Item=T> + Clone,
          T: PartialEq + Debug {
    assert!(expected.clone().count() == actual.clone().count()); // desperation
    for (e, a) in expected.zip(actual) {
        assert_eq!(e, a);
    }
}
Run Code Online (Sandbox Code Playgroud)

BTW(承认 XY 问题):我很高兴了解一个内置断言来比较两个有序集合,但这并不是我真正感到困惑的地方。

Fra*_*gné 6

zip按值获取其参数,因此这就是它们被移动并且之后不能使用它们的原因。

然而,有一个解决方法:有一个by_ref方法返回对迭代器的可变引用,技巧是&mut I where I: Iterator它还实现了Iterator. 因此,您可以在.by_ref()将两个迭代器传递给之前应用它们zip()

fn assert_list_eq<I, T>(mut expected: I, mut actual: I)
    where I: Iterator<Item=T> + Clone,
          T: PartialEq + Debug {
    for (e, a) in expected.by_ref().zip(actual.by_ref()) {
        assert_eq!(e, a);
    }
    // fail if there are any "leftovers"
    assert_eq!(None, expected.next());
    assert_eq!(None, actual.next());
}
Run Code Online (Sandbox Code Playgroud)

也就是说,标准库已经提供了将固定大小数组与Vec. 但这有点棘手,因为PartialEq它的右侧参数是通用的,并且没有足够的实现来使其以“明显”的方式工作。这是一种方法:

#[test]
fn prime_factors_test() {
    assert_eq!([2,3,5][..], prime_factors(30)[..]);
}
Run Code Online (Sandbox Code Playgroud)

[..]Index<RangeFull>使用特征 (..是 的简写)强制将表达式转换为切片RangeFull

另一种方法是仅交换操作数,然后就不需要[..].

#[test]
fn prime_factors_test() {
    assert_eq!(prime_factors(30), [2,3,5]);
}
Run Code Online (Sandbox Code Playgroud)

那是因为标准库提供了impl PartialEq<[T; 3]> for Vec<T>,但没有提供impl PartialEq<Vec<T>> for [T; 3]