我正在尝试编写一个返回某种列表的函数。由于这个函数是纯函数,并且我可能会多次调用它,因此我想缓存结果。为了避免分配,我想返回所有都指向同一内存位置的引用,这样我只需要在每次函数的唯一调用时执行一次分配。
原则上,我可以将每个结果作为向量存储在缓存中,并返回对结果向量切片的引用。这看起来不安全,但(我认为)事实并非如此。如果您从不删除或修改缓存的元素,那么您的切片引用就是安全的。当您添加到缓存时,矢量可能会移动,但它们的切片不应移动。
显然借用检查器不会接受这个解决方案。就其而言,当函数返回时,缓存被借用,并且您不能再次调用它,因为这需要可变借用。
到目前为止,我拥有的最佳解决方案是在此处的操场上,包括如下:
use std::collections::HashMap;
use std::rc::Rc;
fn n_trues(n: usize, cache: &mut HashMap<usize, Rc<[bool]>>) -> Rc<[bool]> {
cache
.entry(n)
.or_insert_with(|| vec![true; n].into())
.clone()
}
fn main() {
let mut cache = HashMap::new();
let zero = n_trues(0, &mut cache);
let one = n_trues(1, &mut cache);
let other_zero = n_trues(0, &mut cache);
assert!(Rc::ptr_eq(&zero, &other_zero));
for x in [zero, one, other_zero].iter() {
println!("{:?}", x);
}
}
Run Code Online (Sandbox Code Playgroud)
满足Rc借用检查器:从缓存中删除不会使其他引用无效,并且您不能改变Rc. 然而,实际的引用计数是多余的:我们确保缓存仅在程序结束时超出范围,因此在此之前缓存的值不会被释放。这是借用检查器可以检查的事情:它可以确保缓存的寿命比所有结果都长。
有没有什么方法可以消除引用计数而不诉诸不安全代码?
如果您从不删除或修改缓存的元素,那么您的切片引用就是安全的。当您添加到缓存时,矢量可能会移动,但它们的切片不应移动。
你的分析对我来说似乎是正确的。
需要明确的是,这意味着不能从中删除任何内容HashMap,也不能从Vec内部添加或删除任何内容HashMap。执行任何操作都可能导致内存被重新分配,从而使引用无效。它也只能起作用,因为它Vec向堆引入了一定程度的间接性,这样就可以保持稳定。
这看起来不安全,但(我认为)事实并非如此。
安全在 Rust 中具有非常特殊的含义。具体来说,某些类型的行为是任何 Rust 代码中都不允许的。即使使用unsafe块,您也不允许调用该行为。
安全 Rust 是所有 Rust 的子集,编译器可以保证它永远不会执行任何上述问题。值得注意的是,这意味着某些类型的代码不会生成未定义的行为,但编译器无法保证。这就是unsafe代码发挥作用的地方。程序员(而不是编译器)需要验证保证不会被破坏,就像在 C 或 C++ 中一样。
不安全的 Rust 代码并不坏,它只是比安全的 Rust 代码需要更多的程序员审查。这仍然比对所有代码应用这种级别的审查(同样,就像在 C 或 C++ 中那样)要好得多。
有没有什么方法可以消除引用计数而不诉诸不安全代码?
我什么都不知道。你能做的就是寻找一个已经为你做这件事的箱子。
我喜欢使用的一个是typed-arena,它允许分配许多具有相同时间长度的东西:
extern crate typed_arena;
use typed_arena::Arena;
use std::collections::HashMap;
fn n_trues<'a>(n: usize, slab: &'a Arena<Vec<bool>>, cache: &mut HashMap<usize, &'a [bool]>) -> &'a [bool] {
cache
.entry(n)
.or_insert_with(|| slab.alloc(vec![true; n]))
.clone()
}
fn main() {
let slab = Arena::new();
let mut cache = HashMap::new();
let zero = n_trues(0, &slab, &mut cache);
let one = n_trues(1, &slab, &mut cache);
let other_zero = n_trues(0, &slab, &mut cache);
assert_eq!(zero.as_ptr(), other_zero.as_ptr());
for x in [zero, one, other_zero].iter() {
println!("{:?}", x);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
952 次 |
| 最近记录: |