#177 Jun 2, 2026

177. BTreeMap::range — Iterate a Sorted Map by Key Range

You have a sorted map keyed by time, port number, or version, and you want every entry between two keys. Filtering on iter() walks the whole map — BTreeMap::range walks just the slice you asked for.

The filter-everything pattern

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
use std::collections::BTreeMap;

let mut map = BTreeMap::new();
map.insert(1, "a");
map.insert(3, "c");
map.insert(5, "e");
map.insert(7, "g");

// Walk every entry, skip the ones you don't want.
let hits: Vec<_> = map.iter().filter(|(k, _)| (3..7).contains(*k)).collect();
assert_eq!(hits, vec![(&3, &"c"), (&5, &"e")]);

Correct, but with a million entries you still traversed all of them. BTreeMap::range takes a RangeBounds and seeks straight to the start key, iterating only through the requested window:

1
2
3
4
5
6
7
use std::collections::BTreeMap;

let map: BTreeMap<i32, &str> =
    BTreeMap::from([(1, "a"), (3, "c"), (5, "e"), (7, "g")]);

let hits: Vec<_> = map.range(3..7).collect();
assert_eq!(hits, vec![(&3, &"c"), (&5, &"e")]);

Same result, log-N lookup to the first key, in-order iteration from there.

All the usual range syntax works

Any RangeBounds<K> is fair game — .., a..b, a..=b, ..b, a..:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use std::collections::BTreeMap;

let map: BTreeMap<i32, &str> =
    BTreeMap::from([(1, "a"), (3, "c"), (5, "e"), (7, "g")]);

let inclusive: Vec<_> = map.range(3..=7).collect();
assert_eq!(inclusive, vec![(&3, &"c"), (&5, &"e"), (&7, &"g")]);

let from_five: Vec<_> = map.range(5..).collect();
assert_eq!(from_five, vec![(&5, &"e"), (&7, &"g")]);

let up_to_five: Vec<_> = map.range(..5).collect();
assert_eq!(up_to_five, vec![(&1, &"a"), (&3, &"c")]);

For an exclusive start (rare but it happens), reach for Bound::Excluded:

1
2
3
4
5
6
7
8
use std::collections::BTreeMap;
use std::ops::Bound::{Excluded, Unbounded};

let map: BTreeMap<i32, &str> =
    BTreeMap::from([(1, "a"), (3, "c"), (5, "e"), (7, "g")]);

let after_three: Vec<_> = map.range((Excluded(3), Unbounded)).collect();
assert_eq!(after_three, vec![(&5, &"e"), (&7, &"g")]);

Mutate in place with range_mut

range_mut gives you &mut V for each entry in the window — keys stay immutable (they have to, the map’s invariant depends on them):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use std::collections::BTreeMap;

let mut scores: BTreeMap<i32, i32> =
    BTreeMap::from([(1, 10), (3, 10), (5, 10), (7, 10)]);

for (_, v) in scores.range_mut(3..=5) {
    *v += 1;
}

assert_eq!(scores[&1], 10);
assert_eq!(scores[&3], 11);
assert_eq!(scores[&5], 11);
assert_eq!(scores[&7], 10);

The same trick works on BTreeSet::range.

Takeaway

When you find yourself filtering a BTreeMap by a range of keys, swap the filter for range. You get O(log n) seek plus O(k) iteration over just the matches, and the code reads more like the intent.

← Previous 176. Option::as_deref — Stop Writing .as_ref().map(|s| s.as_str())