On 04 Mar 2014, at 23:05, Daniel Micay <danielmi...@gmail.com> wrote:
> 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. I assumed the compiler might be able to optimize the extra check away if the .next() method gets inlined, but not in the general case. Thus I was trying to provide a general solution that wouldn't rely on back-end optimizations. Are you saying I assumed wrong?
_______________________________________________ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev