Ordered Collections

range()

The .range(range) method on BTreeMap<K, V> and BTreeSet<T> returns a lazy, double-ended iterator over all entries whose keys fall within the specified bounds.

Signatures on BTreeMap and BTreeSet

// On BTreeMap<K, V>:
pub fn range<T, R>(&self, range: R) -> std::collections::btree_map::Range<'_, K, V>
where
    T: Ord + ?Sized,
    K: Borrow<T> + Ord,
    R: RangeBounds<T>;

// On BTreeSet<T>:
pub fn range<K, R>(&self, range: R) -> std::collections::btree_set::Range<'_, T>
where
    K: Ord + ?Sized,
    T: Borrow<K> + Ord,
    R: RangeBounds<K>;

Because K: Borrow<T> and T: Ord + ?Sized, you can pass a range of borrowed &str slices (map.range("a".."m")) to a BTreeMap<String, V> without allocating String endpoints!

Querying BTreeMap<String, V> with &str Range Endpoints

Note a subtle Rust syntax rule with unsized str: when querying a BTreeMap<String, V> with &str literals, either pass String references or specify (Bound<&str>, Bound<&str>) / .range::<str, _>(...) when the compiler needs to know whether T is str or &str:

use std::collections::BTreeMap;

fn main() {
    let directory = BTreeMap::from([
        (String::from("alice"), "eng"),
        (String::from("bob"), "sales"),
        (String::from("carol"), "eng"),
        (String::from("dave"), "ops"),
        (String::from("erin"), "sec"),
    ]);

    // Zero-allocation range query on `BTreeMap<String, &str>` using `T = str`:
    println!("Names from 'b' through 'd':");
    for (name, dept) in directory.range::<str, _>("b"..="d") {
        println!("  {name:<6} ({dept})");
    }
}
Names from 'b' through 'd':
  bob    (sales)
  carol  (eng)
  dave   (ops)

Reverse Iteration Over a Subrange (.range(...).rev())

Because btree_map::Range and btree_set::Range implement DoubleEndedIterator, calling .rev() walks the subrange from the upper bound down to the lower bound in $O(\log n + k)$ time:

use std::collections::BTreeMap;

fn main() {
    let events = BTreeMap::from([
        (10, "init"),
        (20, "connect"),
        (30, "query"),
        (40, "close"),
    ]);

    let recent_first: Vec<(&i32, &&str)> = events.range(15..=35).rev().collect();
    println!("Events in 15..=35 (newest first): {recent_first:?}");
}
Events in 15..=35 (newest first): [(30, &"query"), (20, &"connect")]

Runtime Panic Conditions of .range()

.range(start..end) panics at runtime if the lower bound is greater than the upper bound (start > end), or if both bounds are Bound::Excluded(x) with the exact same value (Excluded(x), Excluded(x)) (which represents an impossible empty open interval $(x, x)$):

// Panics at runtime: `range start is greater than range end in BTreeMap`
// map.range(50..10);