Is this "the way" (PhantomData and polymorphism)?

I tried to implement two permutation iterators, one which returns references to the input data in the permutations, the other clones the data. I tried to make this as generic as possible and to reuse the core iterator function next() in both cases (as the only difference would be putting a referenced vs. a cloned value into the next result vector). I never used PhantomData before, but the compiler strongly suggested to use it.

Now my question: is this the right way to do it? Is there a simpler way?

The code is here:

Github: Permutations

Instead of providing exactly “cloned” and “uncloned”, I'd make permute() take a user-supplied function which produces the item to be put in the Vec. Then there will be no special purpose trait (just FnMut(&T) -> U) and no PhantomData (because the function is an actual, not phantom, value). The function can then be Clone::clone or the identity function to produce your two desired results, or other things such as copying only one field from the input.

The general principle here is: if you have to pay the cost of allocating a data structure to return to your caller, let the caller control what goes in that data structure, so that they don't have to reallocate to convert it to what they actually need.

1 Like

That would be the next level of generalisation, but while working toward what I have, I got in trouble supplying a general function Fn(&T) -> B to any B into the base struct Permutator. As that was my original idea working toward generalisation: do this, and then create three permute functions, one uncloned, one cloned, and one where the user provides a function that may map the value &T to the universe. But I did not get it working.

Here's one version.

I also got rid of -> impl Iterator as returning nominal types is generally nicer for the caller (but does lock you in to some things by exposing more details).

1 Like

Thank you. Although your code looks very similar to my attempts to achieve the same, I guess I learned where I went wrong.

Although I somewhat disagree on making the iterator struct public and the result of permute. What's the benefit? I can see disadvantages only.

The caller will have access to all the other traits the iterator implements (eg. with just impl Iterator, you'll lose access to ExactSizeIterator, DoubleEndedIterator, or even plain old Clone, Debug, etc.)

1 Like

Also,

  • impl Trait types are unnameable (somewhat less important in the face of a closure generic)
  • you can't implement your own traits on impl Trait types directly
  • impl Trait type's generic parameters are always invariant
  • impl Trait types are always treated as if they have non-trivial destructors
  • Every impl Trait is considered unique, even if the hidden type is the same (this will loosen up eventually)
  • probably other things I'm not thinking of offhand

I think the only advantages to the caller are indirect (more optimization possible when boxed type erasure can be avoided).

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.