首页 > 解决方案 > 如何在容器内保存对不同元素的多个引用?

问题描述

考虑这个简单的例子:v: Vec<Vec<i32>>我想添加v[1]v[0].

我什至没有考虑牺牲性能,因此克隆任何向量都不是一种选择。因此,无论我们如何准确地实现向量的相加,我们都需要在v:&mut v[0]&v[1]. 显然,这里的问题是索引借用v和可变上下文中v的索引可变地借用,因此借用检查器不允许这样做。

这个例子导致了一个更普遍的问题:如果所有返回(可变)对元素(可变)引用的方法都借用了容器本身,我们如何才能同时引用容器的不同元素?

请注意,我确实了解问题的根源以及为什么在编译时借用检查器看不到我们引用了不同的元素。问题是我们如何在不牺牲性能和/或安全性的情况下告诉编译器我们正在做正确的事情?

目前我知道 3 种可能的解决方案没有明显的性能开销:

  1. slice::split_at_mut是一个不错的解决方法,但不幸的是仅适用于顺序/类似切片的容器。请注意,此函数在实现中使用了不安全的代码。
  2. 使用迭代器:是的,我们可以同时保存从迭代器返回的引用,但是在许多情况下使用迭代器是不正确的,例如,map: HashMap<i32, Vec<i32>>我再次想添加map[1]map[0]. 请注意(据我所知)迭代器也使用不安全代码(直接或间接)。
  3. 最后是适用于每个容器的解决方案:RefCell<T>,即将容器的元素包装在RefCell. (好吧,或者Cell<T>在某些情况下)。不过这个有两个问题。首先是运行时借用检查的轻微性能开销。第二个是,例如,如果我正在编写一个返回容器的函数,我要么必须让调用者使用我的RefCell-wrapped 容器,要么必须复制整个容器以删除RefCell包装(是否一种RefCell免费打开容器内部包装的方法?)再一次,RefCell使用不安全的代码。

所有这些解决方案都使用不安全的代码。这真的是唯一的方法吗?我也很确定有一个直接使用不安全代码的解决方案,但作为初学者,我有点害怕深入研究不安全的 Rust。不过,如果它碰巧是一个好的解决方案,请指出我需要研究的主题。

还有其他解决方案吗?哪一个更实用?如有不明白或理解错误的地方,请纠正我。

编辑:正如 Sven Marnach 指出的那样,我的问题太宽泛了,所以我正在缩小问题的范围。
我有一个map: HashMap<i32, Vec<i32>>并且我想以零性能开销分配map[0] + map[1](按元素添加) 。map[0]在这里,matiu 建议的重复索引不是最佳的,因为它涉及对同一个键的多次搜索。那么,有可能吗?如果不是,这种情况的最佳解决方案是什么?

标签: rust

解决方案


所以目标是用 vec[0] + vec[1] 覆盖 vec[0] ?

我认为诀窍是在 Vec 中使用索引,而不是保持引用打开。

这是否达到目标?

fn main() {
    let mut vec = vec![
        vec![1, 2, 3],
        vec![10, 20, 30],
    ];
    let ln = vec[0].len();
    for i in 0..ln {
        vec[0][i] += vec[1][i];
    }
    println!("{:?}", vec);
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ea1900726ff07b5d2bd0ad39f15e1bba


我还想测试就地执行是否比创建新数组更快。“也许编译器足够聪明,可以重复使用内部存储器”我想。事实证明编译器并不那么聪明。

带索引的 for 循环是最快的方法。

代码:https ://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=1d57a0e5bbb21b3bb87dfc5a7735335f

结果(在我的笔记本电脑上):

running 7 tests
test bench_create_new_array   ... bench:         230 ns/iter (+/- 0)
test bench_for_indexes        ... bench:         174 ns/iter (+/- 0)
test bench_new_array_borrow   ... bench:         231 ns/iter (+/- 0)
test bench_to_owned1          ... bench:       1,097 ns/iter (+/- 4)
test bench_to_owned_in_place  ... bench:         240 ns/iter (+/- 1)
test bench_to_owned_in_place2 ... bench:       1,080 ns/iter (+/- 159)
test bench_to_owned_in_place3 ... bench:       1,037 ns/iter (+/- 2)

推荐阅读