英文:
Easier way to return `impl Iterator<Item = T>` with `DoubleEndedIterator`, `ExactSizeIterator` and `FusedIterator` features
问题
在Rust中,你可以在返回签名中使用impl Trait
来简化返回类型。此外,它允许你静态地表示闭包的“未命名具体类型”。
在签名中只看到impl Iterator<Item = T>
是相当常见的,这比确切的返回类型要容易得多。此外,我认为这还可以使你的API对返回的迭代器类型的任何更改保持稳定。例如,你可以将Vec
(std::slice::Iter
)替换为VecDeque
(std::collections::vec_deque::Iter
)。
然而,我刚刚发现这有一个显著的缺点。在你需要访问DoubleEndedIterator
、ExactSizeIterator
和FusedIterator
提供的方法的情况下,你意外地减少了返回的迭代器的功能。
考虑这个示例在Rust Playground上...
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct NewVec<T>(Vec<T>);
impl<T> NewVec<T> {
pub fn iter(&self) -> impl Iterator<Item = &T> + DoubleEndedIterator {
self.0.iter()
}
}
fn main() {
let palindrome = NewVec(Vec::from(['R', 'A', 'C', 'E', 'C', 'A', 'R', '!']));
println!("{:?}", palindrome);
println!(
"{}",
palindrome
.iter()
.map(|character| format!("{}", character))
.collect::<Vec<String>>()
.join("")
);
println!(
"{}",
palindrome
.iter()
.rev()
.map(|character| format!("{}", character))
.collect::<Vec<String>>()
.join("")
);
}
在NewStruct
上的iter
方法不能只返回impl Iterator<Item = &T>
,而必须返回impl Iterator<Item = &T> + DoubleEndedIterator
以使rev
方法工作。
这并不是世界末日,但现在签名需要impl Iterator<Item = &T> + DoubleEndedIterator + ExactSizeIterator + FusedIterator
,以避免降低返回的迭代器的功能。
是否有更好的方法来实现这一点?如果我不想限制底层迭代器,那么是否真的需要在每个返回迭代器的函数返回类型上添加impl Iterator<Item = &T> + DoubleEndedIterator + ExactSizeIterator + FusedIterator
?
这是否也意味着,即使底层迭代器实际上实现了这些特性,如果返回的迭代器没有指定它也实现了ExactSizeIterator
和FusedIterator
,那么在某些情况下可能会有性能影响?
目前还没有办法创建trait别名。类型别名不适用于trait,而通过trait继承可以模拟trait别名,但这会引入一个新类型,现在需要导出该新类型。
英文:
In Rust you can use impl Trait
in a return signature to simplify the return type. In addition, it allows you to statically represent the "unnamed concrete type" of closures.
It is pretty common to just see impl Iterator<Item = T>
in the signature, which is much easier to write than the exact return type. In addition, I believe this also makes your API stable against any change to the type of the returned iterator. For instance, you could swap out a Vec
(std::slice::Iter
) for a VecDeque
(std::collections::vec_deque::Iter
).
However I just discovered that this has a significant drawback. In the case that you need access to methods provided by DoubleEndedIterator
, ExactSizeIterator
and FusedIterator
you have now accidently reduced the features of your returned iterator.
Consider this example on the Rust playground...
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct NewVec<T>(Vec<T>);
impl<T> NewVec<T> {
pub fn iter(&self) -> impl Iterator<Item = &T> + DoubleEndedIterator {
self.0.iter()
}
}
fn main() {
let palindrome = NewVec(Vec::from(['R', 'A', 'C', 'E', 'C', 'A', 'R', '!']));
println!("{:?}", palindrome);
println!(
"{}",
palindrome
.iter()
.map(|character| format!("{}", character))
.collect::<Vec<String>>()
.join("")
);
println!(
"{}",
palindrome
.iter()
.rev()
.map(|character| format!("{}", character))
.collect::<Vec<String>>()
.join("")
);
}
The iter
method on NewStruct
cannot just return impl Iterator<Item = &T>
, instead must return impl Iterator<Item = &T> + DoubleEndedIterator
in order for rev
to work.
This isn't the end of the world, but now signatures are impl Iterator<Item = &T> + DoubleEndedIterator + ExactSizeIterator + FusedIterator
in order to avoid a reduction in the features of the returned iterator.
Is there a better way to achieve this? Do I really have to add impl Iterator<Item = &T> + DoubleEndedIterator + ExactSizeIterator + FusedIterator
on every function return type that returns an iterator if I do not want to restrict the underlying iterator?
Does this also imply that there may also be a performance impact in cases whereby the returned iterator does not specify that it also implements ExactSizeIterator
and FusedIterator
, even though underlying iterator actually does implement these traits?
There is no way to make trait aliases at the moment. Type aliases do not work for traits and a trait alias can be emulated with trait inheritance, however this introduces a new type which now needs to be exported.
答案1
得分: 2
你可以省略FusedIterator
,因为根据文档的说明,它不应该在边界中使用,而应该作为fuse()
中的特化使用,并且特化可以“看穿”impl Trait
。
你也可以省略Iterator
,因为它是DoubleEndedIterator
和ExactSizeIterator
的超级trait。
这样我们就得到了以下更简短的边界:
pub fn iter(&self) -> impl DoubleEndedIterator<Item = &T> + ExactSizeIterator {
self.0.iter()
}
英文:
You can omit FusedIterator
, since, as noted in the docs, it should not be used in bounds but rather used as specialization in fuse()
, and specializations can "see through" impl Trait
.
You can also omit Iterator
, since it is a supertrait of both DoubleEndedIterator
and ExactSizeIterator
.
Which leaves us with these shorter bounds:
pub fn iter(&self) -> impl DoubleEndedIterator<Item = &T> + ExactSizeIterator {
self.0.iter()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论