gpt4 book ai didi

rust - 将可迭代传递给函数并在 Rust 中迭代两次

转载 作者:行者123 更新时间:2023-12-02 16:06:55 26 4
gpt4 key购买 nike

我有一个函数看起来像

fn do_stuff(values: HashSet<String>) {
// Count stuff
for s in values.iter() {
prepare(s);
}
// Process stuff
for s in values.iter() {
process(s);
}
}

这很好用。对于单元测试,我想传递一个二值集合,其中元素以已知顺序传递。 (以其他顺序处理它们不会测试我要测试的情况。)HashSet 不保证顺序,所以我想传递一个 Vec相反。

我想将参数更改为 Iterable,但似乎只有 IntoIter 存在。我试过了

fn do_stuff<C>(values: C)
where C: IntoIterator<Item=String>
{
// Count stuff
for s in values {
prepare(s);
}
// Process stuff
for s in values {
process(s);
}
}

失败是因为第一次迭代消耗了。编译器建议借用 values,但是

fn do_stuff<C>(values: C)
where C: IntoIterator<Item=String>
{
// Count stuff
for s in &values {
prepare(s);
}
// Process stuff
for s in values {
process(s);
}
}

失败是因为

the trait Iterator is not implemented for &C

我可能可以用 clone 做一些工作,但实际的集合会很大,我想尽可能避免复制它。

考虑到这一点,签名可能应该是 do_stuff(values: &C),所以如果这样可以使问题更简单,那么这是一个可以接受的解决方案。

所以建议 Writing a generic function that takes an iterable container as parameter in Rust作为一个相关的问题,但这是一个终生的问题。我的生命周期没有问题。

看起来像How to create an `Iterable` trait for references in Rust?可能实际上是解决方案。但是我无法编译它。

我的第一次尝试是


pub trait Iterable {
type Item;
type Iter: Iterator<Item = Self::Item>;
fn iterator(&self) -> Self::Iter;
}

impl Iterable for HashSet<String> {
type Item = String;
type Iter = HashSet<String>::Iterator;
fn iterator(&self) -> Self::Iter {
self.iter()
}
}

失败了

error[E0223]: ambiguous associated type
--> src/file.rs:178:17
|
178 | type Iter = HashSet<String>::Iterator;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<HashSet<std::string::String> as Trait>::Iterator`

按照该建议:

impl Iterable for HashSet<String> {
type Item = String;
type Iter = <HashSet<std::string::String> as Trait>::Iterator;
fn iterator(&self) -> Self::Iter {
self.iter()
}
}

失败了

error[E0433]: failed to resolve: use of undeclared type `Trait`
--> src/file.rs:178:50
|
178 | type Iter = <HashSet<std::string::String> as Trait>::Iterator;
| ^^^^^ use of undeclared type `Trait`

rust 文档似乎不包含 Trait 作为已知类型。如果我将 Trait 替换为 HashSet,它不会将 IteratorIntoIter 识别为表达式中的最终值.

实现接受的答案

尝试实现@eggyal 的答案,我能够编译它

use std::collections::HashSet;

fn do_stuff<I>(iterable: I)
where
I: IntoIterator + Copy,
I::Item: AsRef<str>,
{
// Count stuff
for s in iterable {
prepare(s.as_ref());
}
// Process stuff
for s in iterable {
process(s.as_ref());
}
}

fn prepare(s: &str) {
println!("prepare: {}", s)
}
fn process(s: &str) {
println!("process: {}", s)
}

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

#[test]
fn doit() {
let vec: Vec<String> = vec!["a".to_string(), "b".to_string(), "c".to_string()];
let set = vec.iter().cloned().collect::<HashSet<_>>();
do_stuff(&vec);
do_stuff(&set);
}
}

有这个输出

 ---- simple::test_cluster::doit stdout ----
prepare: a
prepare: b
prepare: c
process: a
process: b
process: c
prepare: c
prepare: b
prepare: a
process: c
process: b
process: a

最佳答案

IntoIterator不仅由集合类型本身实现,而且在大多数情况下(包括 VecHashSet)它也由它们的借用实现(产生一个迭代器借来的元素)。此外,不可变借用总是 Copy .所以你可以这样做:

fn do_stuff<I>(iterable: I)
where
I: IntoIterator + Copy,
I::Item: AsRef<str>,
{
// Count stuff
for s in iterable {
prepare(s);
}
// Process stuff
for s in iterable {
process(s);
}
}

然后通过传入相关集合的借用来调用它:

let vec = vec!["a", "b", "c"];
let set = vec.iter().cloned().collect::<HashSet<_>>();
do_stuff(&vec);
do_stuff(&set);

Playground .

但是,根据您的要求(是否必须先准备所有项目才能处理任何项目),在这种情况下,可以将准备和处理合并到迭代器的单次传递中。

关于rust - 将可迭代传递给函数并在 Rust 中迭代两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69165079/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com