Search code examples
rustiteratorbtreemap

How to efficiently clone and truncate a BTreeMap?


I'd like to efficiently, preferably in a functional way, truncate an existing BTreeMap from a reference and return a cloned, truncated BTreeMap of the same key-value pairs.

use std::collections::BTreeMap;

/// I want to return a BTreeMap containing only the first 2 keys.
fn truncate(b: &BTreeMap<u32, u32>) -> BTreeMap<u32, u32> {
    b.iter().take(2).cloned().collect()
}

fn main() {
    let mut b = BTreeMap::new();
    b.insert(1, 1);
    b.insert(2, 1);
    b.insert(3, 1);
    let t = truncate(&b);
}

The error message:

expected an iterator that yields &_, but it yields (&u32, &u32)


Solution

  • It might be useful to consider all error messages (Playground):

    error[E0271]: expected `std::iter::Take<std::collections::btree_map::Iter<'_, u32, u32>>` to be an iterator that yields `&_`, but it yields `(&u32, &u32)`
     --> src/main.rs:5:5
      |
    5 |     b.iter().take(2).cloned().collect()
      |     ^^^^^^^^^^^^^^^^ ------ required by a bound introduced by this call
      |     |
      |     expected reference, found tuple
      |
      = note: expected reference `&_`
                     found tuple `(&u32, &u32)`
    note: required by a bound in `cloned`
     --> /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/iter/traits/iterator.rs:3271:32
      |
      = note: required by this bound in `cloned`
    

    .cloned() will turn an iterator over &T into an iterator over T. But you have an iterator over (&u32, &u32), which .cloned() simply can't be used on. Even if you had &(&u32, &u32), you'd only get (&u32, &u32).

    In this case, there is no neat function that you could use to take care of stripping the references, you'll need to write it yourself.

    b.iter().take(2).map(|(&a, &b)| (a, b)).collect()