考虑标准库中的
他们有很多相似之处,因为
为什么它们结构不同?
我唯一看到的拆分trait的好处是
考虑到
Iterator
trait:pub trait Iterator {
type Item;
// required
pub fn next(&mut self) -> Option<Self::Item>;
// potentially advantageous to override
pub fn size_hint(&self) -> (usize, Option<usize>) { ... }
pub fn count(self) -> usize { ... }
pub fn last(self) -> Option<Self::Item> { ... }
pub fn advance_by(&mut self, n: usize) -> Result<(), usize> { ... }
pub fn nth(&mut self, n: usize) -> Option<Self::Item> { ... }
// convenience
pub fn step_by(self, step: usize) -> StepBy<Self> { ... }
pub fn chain<U>(self, other: U) -> Chain<Self, U::IntoIter> { ... }
pub fn zip<U>(self, other: U) -> Zip<Self, U>::IntoIter> { ... }
pub fn map<B, F>(self, f: F) -> Map<Self, F> { ... }
pub fn for_each<F>(self, f: F) { ... }
...
}
考虑来自futures crate的Stream
和StreamExt
traits:
pub trait Stream {
type Item;
// required
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
// potentially advantageous to override
fn size_hint(&self) -> (usize, Option<usize>) { ... }
}
pub trait StreamExt: Stream {
// convenience
pub fn next(&mut self) -> Next<'_, Self> { ... }
pub fn into_future(self) -> StreamFuture<Self> { ... }
pub fn map<T, F>(self, f: F) -> Map<Self, F> { ... }
pub fn enumerate(self) -> Enumerate<Self> { ... }
pub fn filter<Fut, F>(self, f: F) -> Filter<Self, Fut, F> { ... }
...
}
impl<T> StreamExt for T where T: Stream { ... }
他们有很多相似之处,因为
Stream
本质上是Iterator
的异步版本。然而,我想引起你对它们之间的差异的注意。为什么它们结构不同?
我唯一看到的拆分trait的好处是
StreamExt
方法不能被覆盖。这样它们就可以保证按照预期的行为,而Iterator
的便利方法可能会被覆盖以显示不一致的行为。然而,我无法想象这是一个常见的问题,需要考虑对其进行保护。这种差异是以可访问性和可发现性为代价的,需要用户导入StreamExt
才能使用它们,并首先知道它们的存在。考虑到
Stream
是在Iterator
之后出现的,显然这种拆分是一个有意识的决定,但是动机是什么呢?肯定不止我想到的那些。Iterator
设计有什么不好吗?
当由另一个crate提供时,扩展trait肯定是必需的,但这个问题不是关于那个的。