【发布时间】:2021-11-08 21:18:31
【问题描述】:
我有一个看起来像的函数
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。编译器建议借用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);
}
}
失败是因为
特征
Iterator没有为&C实现
我可能会用clone 做一些工作,但实际的集合会很大,如果可能的话我想避免复制它。
考虑一下,签名可能应该是do_stuff(values: &C),所以如果这样可以使问题更简单,那么这是一个可以接受的解决方案。
SO 建议将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,它不会将Iterator 或IntoIter 识别为表达式中的最终值。
接受答案的实施
尝试实现@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
【问题讨论】:
标签: rust