1

Let's say I have a &mut std::collections::HashMap, and I want to turn all the keys into uppercase. The following code does the trick:

use std::collections::HashMap;

fn keys_to_upper<T>(map: &mut HashMap<String, T>) {
    let mut tmp = Vec::with_capacity(map.len());
    for (key, val) in map.drain() {
        tmp.push((key.to_ascii_uppercase(), val));
    }
    for (key, val) in tmp {
        map.insert(key, val);
    }
}

Unfortunately, I don't have a HashMap but a &mut serde_json::Map, and I want to turn all the keys into uppercase. There is no .drain() method. I could use .into_iter() instead, but that would only give me mutable references to the keys and values. To insert them into the map again I would have to clone them, which would hurt performance.

Is there some way here to get around the absense of the .drain() method?

Anders
  • 8,307
  • 9
  • 56
  • 88

1 Answers1

4

A nice tool in your Rust programmer toolbox: std::mem::take. This lets you change a &mut T to a T if the type implements default (if it doesn't, but the type still has a dummy/cheap value you can use, then std::mem::replace is your function of choice).

Applied to your current use-case this gives:

use serde_json::{Map, Value};

fn keys_to_upper<T>(map: &mut Map<String, Value>) {
    *map = std::mem::take(map)
        .into_iter()
        .map(|(k, v)| (k.to_ascii_uppercase(), v))
        .collect();
}
mcarton
  • 27,633
  • 5
  • 85
  • 95