Bos*_*osh 10 rust borrow-checker
我编写了以下代码(+ demo)来删除HashMap基于值的条目.它有效,但我觉得我正在努力反对借用检查器使用:
clone() 避免两次引用同一组键let tmp =绑定,以增加我的临时值的生命周期use std::collections::HashMap;
fn strip_empties(x: &mut HashMap<String, i8>) {
let tmp = x.clone();
let empties = tmp
.iter()
.filter(|&(_, &v)| v == 0)
.map(|(k, _)| k);
for k in empties { x.remove(k); }
}
fn main() {
let mut x: HashMap<String, i8> = HashMap::new();
x.insert("a".to_string(), 1);
x.insert("b".to_string(), 0);
strip_empties(&mut x);
println!("Now down to {:?}" , x);
}
Run Code Online (Sandbox Code Playgroud)
有没有更清洁,更惯用的方法来实现这一目标?
Joh*_*nes 16
其他答案已过时。从 Rust 1.27 开始,您可以使用HashMap::retain来仅保留您感兴趣的元素。您可以使用闭包指定要继续使用的元素。
x.retain(|_, v| *v != 0);
Run Code Online (Sandbox Code Playgroud)
lum*_*max 12
为什么HashMap的变异?只需创建一个新的(所有冰雹不变性):
fn strip_empties(x: HashMap<String, i8>) -> HashMap<String, i8> {
return x.into_iter()
.filter(|&(_, v)| v != 0)
.collect();
}
Run Code Online (Sandbox Code Playgroud)
当然,你必须考虑你的用例.如果你有一个大的HashMap或过滤很多/很少的元素,最好的方法可能会有所不同.让我们比较实现.
use std::collections::HashMap;
fn strip_empties_mutable(x: &mut HashMap<String, i8>) {
let empties: Vec<_> = x
.iter()
.filter(|&(_, &v)| v == 0)
.map(|(k, _)| k.clone())
.collect();
for empty in empties { x.remove(&empty); }
}
fn strip_empties_immutable(x: HashMap<String, i8>) -> HashMap<String, i8> {
return x.into_iter()
.filter(|&(_, v)| v != 0)
.collect();
}
fn build_hashmap() -> HashMap<String, i8> {
let mut map = HashMap::new();
for chr in "abcdefghijklmnopqrstuvmxyz".chars() {
map.insert(chr.to_string(), chr as i8 % 2);
}
return map;
}
#[cfg(mutable)]
fn main() {
let mut map = build_hashmap();
strip_empties_mutable(&mut map);
println!("Now down to {:?}" , map);
}
#[cfg(immutable)]
fn main() {
let mut map = build_hashmap();
map = strip_empties_immutable(map);
println!("Now down to {:?}" , map);
}
Run Code Online (Sandbox Code Playgroud)
保存为hashmap.rs并运行:
rustc --cfg mutable -O -o mutable hashmap.rs
rustc --cfg immutable -O -o immutable hashmap.rs
Run Code Online (Sandbox Code Playgroud)
如果我们查看不同的运行时(例如使用perf stat -r 1000 ./XXX),我们看不到显着的差异.
但是让我们看一下分配的数量:
valgrind --tool=callgrind --callgrind-out-file=callgrind_mutable ./mutable
valgrind --tool=callgrind --callgrind-out-file=callgrind_immutable ./immutable
callgrind_annotate callgrind_mutable | grep 'je_.*alloc'
callgrind_annotate callgrind_immutable | grep 'je_.*alloc'
Run Code Online (Sandbox Code Playgroud)
callgrind_mutable:
7,000 ???:je_arena_malloc_small [$HOME/hashmap/mutable]
6,457 ???:je_arena_dalloc_bin_locked [$HOME/hashmap/mutable]
4,800 ???:je_mallocx [$HOME/hashmap/mutable]
3,903 ???:je_sdallocx [$HOME/hashmap/mutable]
2,520 ???:je_arena_dalloc_small [$HOME/hashmap/mutable]
502 ???:je_rallocx [$HOME/hashmap/mutable]
304 ???:je_arena_ralloc [$HOME/hashmap/mutable]
Run Code Online (Sandbox Code Playgroud)callgrind_immutable:
5,114 ???:je_arena_malloc_small [$HOME/hashmap/immutable]
4,725 ???:je_arena_dalloc_bin_locked [$HOME/hashmap/immutable]
3,669 ???:je_mallocx [$HOME/hashmap/immutable]
2,980 ???:je_sdallocx [$HOME/hashmap/immutable]
1,845 ???:je_arena_dalloc_small [$HOME/hashmap/immutable]
158 ???:je_rallocx [$HOME/hashmap/immutable]
Run Code Online (Sandbox Code Playgroud)这并不令人惊讶,因为clone()可变方法中的调用也会分配内存.当然,可变版本可能会产生具有更大容量的HashMap.
由于借用限制,无法在迭代期间从hashmap中删除值(既不通过remove,也不通过Entryapi),因此您的想法(收集要移除的键)非常接近正确的解决方案.
您只需要克隆整个哈希表,只收集密钥副本就足够了:
fn strip_empties(x: &mut HashMap<String, i8>) {
let empties: Vec<_> = x
.iter()
.filter(|&(_, &v)| v == 0)
.map(|(k, _)| k.clone())
.collect();
for empty in empties { x.remove(&empty); }
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3638 次 |
| 最近记录: |