关于内部可变性模式的设计问题[关闭]

问题描述 投票:0回答:0

我的程序中有一个缓存,其中包含使用 Serde 序列化的数据。 该缓存类型由嵌套类型组成,其中大部分包含

Vec<String>
String
,因此到处都有大量的堆分配。

因为设计,一个函数拥有我的缓存的所有权,所以值被移动到那里。 但在该函数内部,我执行对其他函数的调用。一个人只需要数据的只读视图,但其他人则期望该 Cache 结构的可变性。

由于所有权规则,我只允许有多个不可变引用,或者同时只有一个可变引用。好的。因此,我最初的方法是简单地复制 Cache 类型,然后按值将缓存传递给第一个函数,并传递对克隆缓存的引用。作品。但是,程序的连续迭代后缓存会增长,所以我看到缓存的负载是如何真正影响性能的。

很明显,我想去掉克隆,这样我就可以将这些结构的内存消耗减少两倍,并且对整体性能的影响更小(当有很多数据时,我不需要克隆这么大的数据结构).即使有引用计数器智能指针的开销,一切都运行得“更好”,或者至少,每次迭代都不会受到 2 倍的影响,因为那个克隆。

好。所以我选择了内部可变性模式。

事实是,现在一切正常。我只需要在所有地方克隆智能指针并在每个正在使用的地方使用

.borrow_mut()
(obv,如果我只使用一个 .borrow() 来解决问题,我的程序会崩溃)。但是,事实是,每次我看到不需要的借用 mut 时,我的眼睛都会有点流血(因为内部嵌套函数只需要读取数据视图)。

所以,我真的很困惑。我应该选择什么?代码的清晰度?探查器向我展示了程序的每次增量迭代都会减少内存消耗,但是那些不必要的

.borrow_mut()
正在杀死我。

我可以寻求更好的解决方案,只需将缓存移动到每个需要的函数中,然后返回它,希望编译器能够很好地应用

RVO
(Rust 中是否存在像 C++ 中那样的返回值优化?)或一些使返回值方式值得付出努力的移动技术。

但我想问,有比智能指针方法更好的解决方案吗?一般来说,返回方式是处理这种令人费解的情况的惯用方式吗?

我只是要求一些关于 Rust 中常见模式和习语的一般指导。稍后,我会做一些测试,然后用手上的分析器来决定。但这是关于 Rust 的一般知识,与所暴露的“通用”相似情况有关。

// 编辑

#[derive(Default)]
struct Cache {
    pub field1: InnerCacheElement,
    pub field2: InnerCacheElement2,
    pub field3: InnerCacheElement3
}

#[derive(Default)]
struct InnerCacheElement {
    pub some_large_owned_string: String,
    pub collection_of_typically_large_strings: Vec<String>
}

#[derive(Default)]
struct InnerCacheElement2 { /* As above, or whatever you prefer, but large owned heap-allocated datatypes*/ }

#[derive(Default)]
struct InnerCacheElement3 { /* As above, or whatever you prefer, but large owned heap-allocated datatypes*/  }



fn main() {
    let cache = Cache::default(); // Instead of this, some generic serde function that loads the cache from a JSON file
    do_main_work(cache); // No further usages of the cache until this point, so I assumed that a move is preferable over a reference (not sure about that)
}

fn do_main_work(cache: Cache) {
    do_inner_work_that_requires_read_only_actions::<String>(&cache);
    do_inner_work_that_writes_to_cache_ds(cache);
    
    /* heavy computations and more processes here */
    save_cache(&cache);  // Cannot move, cache is already borrowed
}

fn do_inner_work_that_requires_read_only_actions<T>(cache: &Cache) -> T 
    where T:Default
{
    let mut t = T::default(); // Just for demonstration purposes
    /* code actions that reads the cache, parses data, and modifies the data contained in t */
    return t;
}

fn do_inner_work_that_writes_to_cache_ds(mut cache: Cache) {
    // Write to the fields, add elements to the containers, create and store new Strings...
}

fn save_cache(cache: &Cache) {
    // Dumps the data in the cache JSON file
}

以这个例子作为元代码,因为缓存已经被借用了,显然不能编译,但这正是重点。

rust reference-counting interior-mutability
© www.soinside.com 2019 - 2024. All rights reserved.