Called
.take_while(..)
on an iterator produced by HashSet::drain
276 let mut area = HashSet::new();
277 loop {
278 if queue.is_empty() {
279 break area.drain().take_while(|pt: &MapPoint| pt.x > 0).collect();280 } else {
281 let last = queue.pop().unwrap_or_else(Default::default);
282 area.insert(last);
Description
Iterator methods such as .skip_while(_)
and .take_while(_)
are usually used
to manipulate a section of an ordered iterator. However, iteration methods upon
std::collections::HashMap
and std::collections::HashSet
iterate in an
arbitrary order, which can result in non-deterministic behaviour when paired
with .skip_while(_)
or .take_while(_)
.
The following methods are typically used upon ordered iterators:
skip_while
take_while
map_while
nth
position
rposition
rev
step_by
last
The following methods produce arbitrarily ordered iterators:
HashMap::iter
orHashSet::iter
HashMap::into_iter
orHashSet::into_iter
HashMap::drain
orHashSet::drain
HashMap::drain_filter
orHashSet::drin_filter
HashMap::keys
HashMap::values
HashMap::into_keys
HashMap::into_values
HashMap::values_mut
HashMap::iter_mut
HashSet::difference
HashSet::intersection
HashSet::symmetric_difference
HashSet::union
Using methods from both categories in conjunction can result in
non-deterministic behaviour. Consider using a structure where the ordering
between elements is well-defined, such as std::collections::BTreeMap
or
std::collections::BTreeSet
.
Bad practice
let mut squares: HashMap<usize, usize> = [(1, 1), (2, 4), (3, 9)].into();
// this may or may not refer to (2, 4)
let second = squares.iter().nth(1).unwrap();
Recommended
let mut squares: BTreeMap<usize, usize> = [(1, 1), (2, 4), (3, 9)].into();
// always refers to (2, 4), the ordering is predictable
let second = squares.iter().nth(1).unwrap();