rust - 在不克隆的情况下使用闭包内的向量
问题描述
我有这个数据结构。
let bucket = HashMap<&str, Vec<&str>>
给定
let cluster = Vec<&str>
我想从Vec
Bucket 上的 s 扩展它,我可以保证我只会访问每个键值对一次,并且&str
incluster
始终是 in 中的一个键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
?
解决方案
您可以消除bucket[seq].clone()
使用std::mem::take()
:
let tmp = tmp.iter().flat_map(
|seq| std::mem::take(bucket.get_mut(seq).unwrap()),
);
这将转移现有的所有权Vec
并在哈希映射中留下一个空的。由于地图仍处于明确定义的状态,因此这是 100% 安全的。由于空向量不分配,因此它也很有效。最后,由于您可以保证不再访问该密钥,因此它是正确的。(游乐场。)
正如评论中所指出的,另一种方法是从哈希图中删除向量,这也转移了向量的所有权:
let tmp = tmp.iter().flat_map(|seq| bucket.remove(seq).unwrap());
外部cluster.clone()
无法替换,take()
因为您需要旧内容。这里的问题是你不能扩展你正在迭代的向量,Rust 不允许这样做以实现有效的基于指针的迭代。这里一个简单而有效的解决方案是使用索引而不是迭代(操场):
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 在安全的情况下非常擅长删除这两种检查,即使没有,索引访问可能仍然比克隆更有效. 确定这是否对您的原始代码有所改进的唯一方法是在生产数据上对两个版本进行基准测试。
推荐阅读
- angular - GitHub 无法从 npm repo 中提取
- apache-nifi - 使用具有不同 URL 的 NiFi 循环 API
- scala - 如何从 spark 应用程序中知道 spark 用户的组名
- ios - 在设置应用程序中看不到 MyAppNotificationSettings 选项
- kendo-ui - 如何在关闭剑道窗口时关闭剑道确认消息?
- javascript - Jquery - 如果所选选项的值设置为特定值,则不运行函数
- mysql - Presto 如何使用 Try_Parse
- php - Symfony - 用数组填充的 ChoiceType
- java - io.netty.util.internal.PlatformDependent0$1 的非法反射访问
- php - PHPMailer:我是否连接到外部 SMPT?