On 04 Mar 2014, at 23:05, Daniel Micay <[email protected]> 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
[email protected]
https://mail.mozilla.org/listinfo/rust-dev