Skip to content

Commit 561caff

Browse files
committed
exactsizeiterator draft
1 parent 2599c9a commit 561caff

File tree

1 file changed

+119
-1
lines changed

1 file changed

+119
-1
lines changed

posts/tour-of-rusts-standard-library-traits.md

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4593,7 +4593,7 @@ trait Iterator {
45934593
type Item;
45944594
fn next(&mut self) -> Option<Self::Item>;
45954595

4596-
// provides default impls for 67 functions
4596+
// provides default impls for 75 functions
45974597
// which are omitted here for brevity's sake
45984598
}
45994599
```
@@ -4731,6 +4731,124 @@ fn receivers_can_be_iterated() {
47314731

47324732

47334733

4734+
### ExactSizeIterator
4735+
4736+
Prerequisites
4737+
- [Self](#self)
4738+
- [Methods](#methods)
4739+
- [Associated Types](#associated-types)
4740+
- [Marker Traits](#marker-traits)
4741+
- [Subtraits & Supertraits](#subtraits--supertraits)
4742+
- [Iterator](#iterator)
4743+
4744+
```rust
4745+
trait ExactSizeIterator: Iterator {
4746+
// provided default impls
4747+
fn len(&self) -> usize;
4748+
fn is_empty(&self) -> bool;
4749+
}
4750+
```
4751+
4752+
The default impls provided by the `Iterator` trait for the methods `size_hint`, `count`, `last`, and `nth` are suboptimal if we know the exact size of the data we're iterating over and have have fast random access into it.
4753+
4754+
To illustrate this point let's start by defining a type called `Range` which impls `Iterator` that we can calculate the exact size of:
4755+
4756+
```rust
4757+
struct Range {
4758+
start: usize,
4759+
end: usize,
4760+
}
4761+
4762+
impl Iterator for Range {
4763+
type Item = usize;
4764+
fn next(&mut self) -> Option<Self::Item> {
4765+
let current = self.start;
4766+
self.start += 1;
4767+
if current < self.end {
4768+
Some(current)
4769+
} else {
4770+
None
4771+
}
4772+
}
4773+
}
4774+
```
4775+
4776+
Here's the default `size_hint` impl that `Range` would get from the `Iterator` trait:
4777+
4778+
```rust
4779+
fn size_hint(&self) -> (usize, Option<usize>) {
4780+
(0, None)
4781+
}
4782+
```
4783+
4784+
It's not useful at all! The lower bound is hardcoded to `0` and the upper bound is hardcoded to `None`, which is the same as saying _"I have no clue how big this iterator is, it can have anywhere from zero to infinity remaining items in it."_
4785+
4786+
Yet we can precisely calculate how many items are remaining in `Range` and provide an actually useful `size_hint` impl:
4787+
4788+
```rust
4789+
impl Iterator for Range {
4790+
// ...
4791+
fn size_hint(&self) -> (usize, Option<usize>) {
4792+
let size = self.end - self.start;
4793+
(size, Some(size))
4794+
}
4795+
}
4796+
```
4797+
4798+
We can also now impl `ExactSizeIterator` for `Range` because it's a marker trait that marks the type as having an accurate `size_hint` impl:
4799+
4800+
```rust
4801+
impl ExactSizeIterator for Range {}
4802+
```
4803+
4804+
We should also provide our own impls for `count`, `last`, and `nth` as their default impls assume the size of the iterator is unknown and rely on repeatedly calling `next`. Here's a simplified version of the default `count` impl as an example:
4805+
4806+
```rust
4807+
fn count(self) -> usize {
4808+
let mut accum = 0;
4809+
while let Some(x) = self.next() {
4810+
accum += 1;
4811+
}
4812+
accum
4813+
}
4814+
```
4815+
4816+
If we had a `Range` of size one million that's one million times the `next` function would have to be called to `count` it! We can do much better:
4817+
4818+
```rust
4819+
impl Iterator for Range {
4820+
// ...
4821+
fn count(self) -> usize {
4822+
self.end - self.start
4823+
}
4824+
}
4825+
```
4826+
4827+
And efficient impls for `last` and `nth`:
4828+
4829+
```rust
4830+
impl Iterator for Range {
4831+
type Item = usize;
4832+
fn last(self) -> Option<Self::Item> {
4833+
if self.start == self.end {
4834+
None
4835+
} else {
4836+
Some(self.end - 1)
4837+
}
4838+
}
4839+
fn nth(&mut self, n: usize) -> Option<Self::Item> {
4840+
if self.start + n > self.end {
4841+
None
4842+
} else {
4843+
self.start += n;
4844+
self.next()
4845+
}
4846+
}
4847+
}
4848+
```
4849+
4850+
4851+
47344852
### IntoIterator
47354853

47364854
Prerequisites

0 commit comments

Comments
 (0)