The problem:
When you iterate over elements of an Iterator in a for-loop, you effectively
end up checking whether the Iterator is empty or not twice per element, i.e.
once inside the Iterator's "next" method and once in doing match for the Option
returned by the "next" method.
Example:
struct Digits {
n: int
}
impl Iterator<int> for Digits {
fn next(&mut self) -> Option<int> {
if self.n < 10 { // Checking whether Iterator is empty or not
let ret = Some(self.n);
self.n += 1;
ret
}
else { None }
}
}
fn main() {
let mut itr = Digits { n: 0 };
for i in itr {
println!("{}", i);
}
}
I assume that the for-loop above is effectively the same as the following:
fn main() {
let mut itr = Digits { n: 0 };
loop {
let x = itr.next(); // The first check happens here
match x { // And the second check happens here
Some(i) => println!("{}", i),
None => break
}
}
}
The proposed solution:
We add to the language the ability to set trait-methods private (accessible
only to default methods of that trait). We add two new private methods to the
Iterator<A> trait, namely "is_empty" and "next_unwrapped". Then we provide a
default implementation for the next method of Iterator<A> trait. It would look
something like the following:
pub trait Iterator<A> {
priv fn is_empty(&self) -> bool;
priv fn next_unwrapped(&mut self) -> A;
fn next(&mut self) -> Option<A> {
if self.is_empty() {
None
}
else {
Some(self.next_unwrapped())
}
}
}
Then the Iterator implementation for Digits we had before would become
something like the following:
impl Iterator<int> for Digits {
// Since we're not overriding the default implementation for the "next"
method,
// we must implement the private methods which the "next" method uses:
priv fn is_empty(&self) -> bool {
self.n > 9
}
priv fn next_unwrapped(&mut self) -> int {
let ret = self.n;
self.n += 1;
ret
}
}
And the for-loop we had in the beginning could be translated under the hood to
something like the following:
fn main() {
let mut itr = Digits { n: 0 };
loop {
if itr.is_empty() {
break;
}
else {
let i = itr.next_unwrapped();
println!("{}", i);
}
}
}
Now we're checking whether or not the iterator is empty only once per element.
And notice also that the code above would not compile because the is_empty()
and next_unwrapped() methods are private to the Iterator trait, but the
compiler is allowed to secretly call them when you write the above main() using
the regular for-loop syntax:
fn main() {
let mut itr = Digits { n: 0 };
for i in itr {
println!("{}", i);
}
}
P.S.
A while ago I suggested adding private trait-methods for a different use case
(namely the non-virtual interface idiom).
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev