0

I am building up a library for generating the minimum perfect hash from a set of keys. The idea is to index the keys online without storing the full dataset in memory. Based on a user requirement, it is possible that skip_next() is not available and I want to fall back to using next(). Although it might be slower based on the speed of the iterator, it simplifies things for a general user.

My idea is to selectively iterate over all the elements generated by an iterator. This code works fine, but it requires a user to implement the trait FastIteration:

#[derive(Debug)]
struct Pixel {
    r: Vec<i8>,
    g: Vec<i8>,
    b: Vec<i8>,
}

#[derive(Debug)]
struct Node {
    r: i8,
    g: i8,
    b: i8,
}

struct PixelIterator<'a> {
    pixel: &'a Pixel,
    index: usize,
}

impl<'a> IntoIterator for &'a Pixel {
    type Item = Node;
    type IntoIter = PixelIterator<'a>;

    fn into_iter(self) -> Self::IntoIter {
        println!("Into &");
        PixelIterator {
            pixel: self,
            index: 0,
        }
    }
}

impl<'a> Iterator for PixelIterator<'a> {
    type Item = Node;
    fn next(&mut self) -> Option<Node> {
        println!("next &");
        let result = match self.index {
            0 | 1 | 2 | 3 => Node {
                r: self.pixel.r[self.index],
                g: self.pixel.g[self.index],
                b: self.pixel.b[self.index],
            },
            _ => return None,
        };
        self.index += 1;
        Some(result)
    }
}

trait FastIteration {
    fn skip_next(&mut self);
}

impl<'a> FastIteration for PixelIterator<'a> {
    fn skip_next(&mut self) {
        self.index += 1;
    }
}

fn main() {
    let p1 = Pixel {
        r: vec![11, 21, 31, 41],
        g: vec![12, 22, 32, 42],
        b: vec![13, 23, 33, 43],
    };

    let mut index = 0;
    let mut it = p1.into_iter();
    loop {
        if index == p1.r.len() {
            break;
        }

        if index == 1 {
            it.skip_next()
        } else {
            let val = it.next();
            println!("{:?}", val);
        }
        index += 1;
    }
}

How can one make the above program fall back to using the normal next() instead of skip_next() based on if the trait FastIteration is implemented or not?

fn fast_iterate<I>(objects: I)
    where I: IntoIter + FastIteration { // should use skip_next() };

fn slow_iterate<I>(objects: I)
    where I: IntoIter { // should NOT use skip_next(), use next() };

As above, one can always write two separate impl but is it possible to do this in one?

This question builds on:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Avi
  • 107
  • 1
  • 8

1 Answers1

1

You are looking for the unstable feature specialization:

#![feature(specialization)]

#[derive(Debug)]
struct Example(u8);

impl Iterator for Example {
    type Item = u8;
    fn next(&mut self) -> Option<u8> {
        let v = self.0;
        if v > 10 {
            None
        } else {
            self.0 += 1;
            Some(v)
        }
    }
}

trait FastIterator: Iterator {
    fn skip_next(&mut self);
}

impl<I: Iterator> FastIterator for I {
    default fn skip_next(&mut self) {
        println!("step");
        self.next();
    }
}

impl FastIterator for Example {
    fn skip_next(&mut self) {
        println!("skip");
        self.0 += 1;
    }
}

fn main() {
    let mut ex = Example(0);
    ex.skip_next();

    let mut r = 0..10;
    r.skip_next();
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366