caching - Rust 中的安全缓存无需重新计数

标签 caching rust

我正在尝试编写一个返回某种列表的函数。由于此函数是纯函数,而且我可能会多次调用它,所以我想缓存结果。为避免分配,我想返回所有指向同一内存位置的引用,这样我只需要在每次调用函数时执行一次分配。

原则上,我可以将每个结果作为向量存储在缓存中,并返回对结果向量切片的引用。这看起来不安全,但(我认为)不是。如果您从不删除或修改缓存的元素,那么您的切片引用是安全的。当您添加到缓存中时,向量可能会移动,但它们的切片不应该移动。

显然,借用检查器不会接受此解决方案。就它而言,缓存在您的函数返回时被借用,您不能再次调用它,因为这需要可变借用。

到目前为止,我拥有的最佳解决方案是在 Playground 上 here , 并包含在下面:

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);
    }
}

Rc 满足借用检查器的要求:从缓存中删除不会使其他引用失效,并且您不能改变 Rc 的内容。然而,实际的引用计数是多余的:我们确保缓存只在程序结束时超出范围,所以缓存的值直到那时才被释放。这是借用检查器可以检查的事情:它可以确保缓存比所有结果都长。

有没有什么方法可以在不诉诸不安全代码的情况下消除引用计数?

最佳答案

If you never delete or modify elements of the cache, then your slice references are safe. The vectors might be moved when you add to the cache, but their slices shouldn't be.

我认为您的分析是正确的。

需要明确的是,这意味着 永远 不能从 HashMap 中删除任何内容,也不能从 Vec 中添加或删除任何内容在 HashMap 中。执行任何操作都可能导致重新分配内存,从而使引用无效。它也只是因为 Vec 引入了一个到堆的间接级别,然后可以稳定。

This looks unsafe, but (I think) isn't.

安全 在 Rust 中具有非常特殊的含义。具体来说,there are types of behavior that are never allowed in any Rust code .即使在使用 unsafe block 时,您也不允许调用该行为。

Safe Rust 是所有 Rust 的子集,编译器可以保证永远不会执行上述任何问题。值得注意的是,这意味着某些类型的代码不会生成未定义的行为,但编译器无法保证。这就是 unsafe 代码发挥作用的地方。程序员而不是编译器需要验证保证永远不会被破坏,就像在 C 或 C++ 中一样。

不安全的 Rust 代码并不,它只需要比安全的 Rust 代码多一个数量级的程序员审查。这仍然比对您的所有代码应用那种级别的审查要好得多(同样,就像您在 C 或 C++ 中所做的那样)。

Is there any way to eliminate the refcount without resorting to unsafe code?

我什么都不知道。你能做的是look for a crate已经为你做了。

我喜欢使用的一个是 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);
    }
}

关于caching - Rust 中的安全缓存无需重新计数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46286899/

相关文章:

.net - 如何使 Entity Framework 4内部缓存无效

session - Cakephp tmp 目录 : Cache and Session Folders

c# - 将 Ruby 转换为 C#

reference - 在创建别名可变借用之后但在使用之前使用不可变借用实际上很危险吗?

macros - 宏可以匹配常量参数而不是文字吗?

ruby-on-rails-3 - 如何在Ruby on Rails 3中缓存查询

php - 后退按钮的 session 问题

performance - 如何根据条件重复向量中的某些元素?

rust - 为什么我的 nom 解析器不消耗整个输入,而留下最后一 block 未解析?

rust - 无法移出 &mut 指针