But looking through the trait implementations, I don't see any reason V should be in there as it is never is returned or used for implementing any traits. I also don't see any mention of this in the documentation for the method that creates these iterators.
I have a situation where I am planning to clone this iterator quite a few times for an internal implementation of cartesian product that doesn't warrant bringing all of itertools. So I wanted to make sure this extra generic wasn't some sort of warning that the iterator was a lot weirder than I was initially imagining.
V is there because the data storage has (or had, or might have) a layout that depends on both K and V, so the iterator code (when monomorphized by the compiler) needs to be able to know both to traverse it properly. If you tried writing your own simple tree map, you'd find yourself nearly required to include V too.
I think that makes sense. I imagine there's a world (filled with bad ideas, surely) where the implementation has extra layers of indirection and the values aren't impacted by the keys because the layout does not store the two together and there could be separate iterators for each.
But you prompted me to go a bit further here and I've come to the realization that .keys (and .values) is just a wrapper around .iter. So cloning .keys is heavier than it appeared at first like I was worried about, as it means cloning all of the values as well even though they are never seen in my code.
Thanks for the quick answer!
edit: Also, if nothing else, .keys being a wrapper around .iter makes it clear that keys would need to have the value as a generic. So a more shallow answer would just be "because that's the way it's implemented, and is simpler that way"
No, .keys()/.values()/.iter() all iterate the map by reference, so no key/value cloning is involved. You just receive references to the keys/values as they are in the map structure. Cloning .iter() is just copying a few pointers.
And for completeness, .keys().cloned() creates a lazy iterator which will clone each key only when iterated over, and no other cloning will be done.
You can see just from the signatures that they don't clone the data:
The Clone implementation for Keys has no bounds — in particular, it has no K: Clone or V: Clone bound. Therefore, the Keys iterator is usable whether or not the keys, or values, implement Clone.
If it did use cloned data, it couldn’t return &K, only K. Iterators cannot return borrows from themselves.
I guess that'll teach me to keep digging when I see the std import from hashbrowns. I got to the use of base::Iter in the std code and figured that was far enough for my purposes.
in particular, it has no K: Clone or V: Clone bound. Therefore, the Keys iterator is usable whether or not the keys, or values, implement Clone.
Absolutely makes sense, I think this is something that I still need to get an eye for spotting quickly. I actually had initially been looking for the trait bounds on Keys before I came here, and then saw that there was an implementation of Clone for it and forgot that I had never found those trait bounds.
If it did use cloned data, it couldn’t return &K, only K. Iterators cannot return borrows from themselves.
I think that I know what you mean, but just to be clear, when you say "return" you mean as the Iterator::Item, which is &'a K. This is definitely something that I need to build up my intuition on better, but makes sense when you point it out.