微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在不克隆的情况下使用闭包内的 Vector

如何解决在不克隆的情况下使用闭包内的 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 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?