如何解决在不克隆的情况下使用闭包内的 Vector
我有这个数据结构。
let bucket = HashMap<&str,Vec<&str>>
给定
let cluster = Vec<&str>
我想从 Bucket 上的 Vec
扩展它,我可以保证我只会访问每个键值对一次,而 &str
中的 cluster
总是bucket
中的一个键。
use std::collections::HashMap;
fn main() {
let mut bucket: HashMap<&str,Vec<&str>> = HashMap::new();
bucket.insert("a",vec!["hello","good morning"]);
bucket.insert("b",vec!["bye","Ciao"]);
bucket.insert("c",vec!["good"]);
let cluster = vec!["a","b"];
let cluster2 = vec!["c"];
let mut clusters = [cluster,cluster2];
clusters.iter_mut().for_each(|cluster| {
// I don't like this clone
let tmp = cluster.clone();
let tmp = tmp.iter().flat_map(|seq| bucket[seq].
clone() // I really don't like this other clone
);
cluster.extend(tmp);
});
println!("{:?}",clusters);
}
这可以编译,但我真正想做的是耗尽 bucket
上的向量,因为我知道我不会再次访问它。
let tmp = tmp.iter().flat_map(|seq| bucket.get_mut(seq).
unwrap().drain(..)
);
error: captured variable cannot escape `FnMut` closure body
--> src/main.rs:13:45
|
4 | let mut bucket: HashMap<&str,Vec<&str>> = HashMap::new();
| ---------- variable defined here
...
13 | let tmp = tmp.iter().flat_map(|seq| bucket.get_mut(seq).
| - ^-----
| | |
| ___________________________________________|_variable captured here
| | |
| | inferred to be a `FnMut` closure
14 | | unwrap().drain(..)
| |______________________________^ returns a reference to a captured variable which escapes the closure body
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore,they cannot allow references to captured variables to escape
我需要去不安全吗?如何?更重要的是,想要删除 clone
是否合理?
解决方法
您可以使用 std::mem::take()
消除 bucket[seq].clone()
:
let tmp = tmp.iter().flat_map(
|seq| std::mem::take(bucket.get_mut(seq).unwrap()),);
这将转移现有 Vec
的所有权并在哈希映射中留下一个空的。由于地图仍处于明确定义的状态,因此这是 100% 安全的。由于空向量不分配,所以它也是有效的。最后,由于您可以保证不再访问该密钥,因此它是正确的。 (Playground.)
正如评论中所指出的,另一种方法是从哈希映射中删除向量,这也会转移向量的所有权:
let tmp = tmp.iter().flat_map(|seq| bucket.remove(seq).unwrap());
外部的 cluster.clone()
不能用 take()
替换,因为您需要旧内容。这里的问题是你不能扩展你正在迭代的向量,为了实现高效的基于指针的迭代,Rust 不允许这样做。这里一个简单而有效的解决方案是使用索引而不是迭代 (playground):
clusters.iter_mut().for_each(|cluster| {
let initial_len = cluster.len();
for ind in 0..initial_len {
let seq = cluster[ind];
cluster.extend(std::mem::take(bucket.get_mut(seq).unwrap()));
}
});
当然,通过索引,你会付出间接和边界检查的代价,但是 rustc/llvm 在安全的情况下非常擅长删除这两者,即使没有,索引访问可能仍然更多效率高于克隆。确定这是否比原始代码有所改进的唯一方法是在生产数据上对两个版本进行基准测试。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。