###For Vec & VecDeque
They are indeed similar, but drain is index focused, whereas retain is value focused. Right now it just seems a waste that drain callers can get the removed values directly, but retain callers have to clone them on their way out.
What to do in case the Retain iterator is leaked? The comments on drain say
I’ll make this more clear in the RFC, but the proposed function will be modifying length each iteration instead of accumulating in del, so the same guarantee holds true.
This does bring up an interesting point, doesn’t forgeting Drain currently leave HashMaps in a broken state? Turns out, yes, yes it does. I’ll file an issue for this.
###For HashMap & HashSet
Wow, I can’t believe I missed that reversing iteration order pretty much eliminates the O(n²) shifts. Given my proposed algorithm isn’t forget resistant, I pretty much have to go with yours (Drain will also probably have to change to something similar). It’s certainly a lot simpler, although it’s still subject to the attack you’ve described.
Speaking of which, iteration order could be somewhat randomized. For example, instead of single pointer to the last full bucket, we could track 2 (or perhaps more) from separate runs (leap-frogging as they hit empty buckets so as to remain separate), and randomly switch between them. I’m not sure if that’s truly random enough to mitigate the issue.
How do you know it’s O(n²) shifts? Hash tables are expected to do better than that. Maybe you meant to say O(n log n).
Just to clarify, the worst case assumes a hash table with 100% collisions and nothing being retained. With 5 items (a, b, c, d, e) that all want to be in bucket 0, if we remove them using pop_internal, shift will be called 4 times for the removal of a, then 3 for the removal of b, 2 for c, 1 for d. That’s (n-1)+(n-2)+...+2+1 or n*(n-1)/2, which is O(n²) .