4

In this example, NoGood is pub, and AtomWord is private.

I'd like to export an instance of IntoIterator, but I can't because this huge type definition for IntoIter includes a reference to AtomWord.

I realize I could create an Iterator wrapper that just passes calls through to the underlying iterator, but that's a lot of boilerplate. I can't think of any way to make the wrapper class generic (without defeating the purpose, which is to hide the AtomWord type).

impl <'a> IntoIterator for &'a NoGood {
    type Item = Literal;
    type IntoIter = FilterMap<slice::Iter<'a, AtomWord>, fn(&AtomWord) -> Option<Literal>>;

    fn into_iter(self) -> Self::IntoIter {
        (&self.lits).into_iter().filter_map(as_opt_lit)
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
dspyz
  • 5,280
  • 2
  • 25
  • 63
  • Actually, the wrapper class solution would be pretty good if Rust had something like GeneralizedNewtypeDeriving. Does it? – dspyz Aug 08 '15 at 16:18
  • 1
    Since it's only one trait with one method that needs to be implemented here, the wrapper solution seems accepted (though not ideal, obviously). –  Aug 08 '15 at 17:24
  • 1
    alternatively box the iterator: `type IntoIter = Box>` – oli_obk Aug 08 '15 at 17:41
  • @dspyz sadly, it doesn't have that functionality (yet?). I'd love something to help with delegation in general and wrapper structs in particular though. – Shepmaster Aug 08 '15 at 20:53
  • This question [talks a bit more](http://stackoverflow.com/a/24835982/155423) about `GeneralizedNewtypeDeriving`. – Shepmaster Aug 09 '15 at 22:15

1 Answers1

2

No, you cannot hide a private type in a public method. It's public, which means that people need to see it.

As delnan mentions, the wrapper struct is common for iterators. It also happens to have zero runtime cost:

struct Iter<'a>(FilterMap<slice::Iter<'a, AtomWord>, fn(&AtomWord) -> Option<Literal>>);

impl<'a> Iterator for Iter<'a> {
    type Item = Literal;
    fn next(&mut self) -> Option<Literal> {
        self.0.next()
    }
}

impl<'a> IntoIterator for &'a NoGood {
    type Item = Literal;
    type IntoIter = Iter;

    fn into_iter(self) -> Self::IntoIter {
        Iter((&self.lits).into_iter().filter_map(as_opt_lit))
    }
}

And as ker mentions, you can box it. This saves programmer typing time, but at the expense of runtime memory allocation:

impl<'a> IntoIterator for &'a NoGood {
    type Item = Literal;
    type IntoIter = Box<Iterator<Item = Literal>>;

    fn into_iter(self) -> Self::IntoIter {
        Box::new((&self.lits).into_iter().filter_map(as_opt_lit))
    }
}

Note that I haven't tried compiling any of these because you didn't provide an MCVE and thus your code doesn't compile anyway.

Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    Iterator has tons of methods. For optimal performance, shouldn't I have implementations of all of them? At least I should have size_hint, shouldn't I? – dspyz Aug 09 '15 at 02:02
  • @dspyz good question! [Right now](https://github.com/rust-lang/rust/issues/24214), there aren't many types in the standard library that effectively implement the iterator methods themselves. However, I think you are right that for better performance, you would forward all those methods to the inner field. I think this is what your `GeneralizedNewtypeDeriving` would help with... – Shepmaster Aug 09 '15 at 15:08