On 04/03/14 03:51 PM, Tommi wrote:
> 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).

There will be no benefit in optimized builds from changing the iterator
design. The only performance issue is caused by a known and fixable
limitation, which is that rustc is not communicating when pointers are
known to be non-null.


Attachment: signature.asc
Description: OpenPGP digital signature

_______________________________________________
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to