当已知可变值不可变时,借用一些可变值两次

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

我有一个类似于以下的构造,其中

Analysis
结构负责分析文件,而
Project
结构负责管理文件和依赖项,作为最小的可重现示例:

struct File;

trait Project {
    fn get_or_insert(&mut self) -> &File;
}

struct Analysis<'a> {
    project: &'a mut dyn Project
}

impl Analysis<'_> {
    fn analyze(&mut self) {
        let file = self.project.get_or_insert();
        self.analyze_parsed_file(file);
    }

    pub fn analyze_parsed_file(
        &mut self,
        file: &File
    ) {}

}

我想做的是分析一个文件。该项目内部有一个加载文件的内存列表,但如果我想要获取的文件不在该列表内,该项目将从磁盘加载它并将其保存到内存中的表示形式。保存是必要的(在我的例子中是为了循环依赖分析)。

提供的示例无法编译,因为我试图多次借用

self
作为可变对象。我的理由如下(但也许这已经是我对借用检查器的理解的极限):

如果我调用

analyze_parsed_file
,该函数可以通过对
file
的可变引用来修改
project
。因此,该示例在 Rust 中是非法的。

现在我知道这永远不会发生,因为该项目永远不会改变文件 - 它只会将它们添加到内部存储中,并且不会覆盖我刚刚获得的文件。

因此,我的问题基本上是:我如何告诉借用检查器?可能可以通过使用某种形式的

unsafe
来解决这个问题,但我宁愿留在安全领域,因为这似乎是一个相当简单的用例。这就是不祥的
RefCell
发挥作用的地方吗?

rust borrow-checker
1个回答
0
投票

正如我在评论中所说,您需要使

Project::get_or_insert()
需要
&self
而不是
&mut self

我们如何做到这一点,同时它需要变异

self

关键是这个:

该项目永远不会改变文件 - 它只会将它们添加到内部存储中,并且不会覆盖我刚刚获得的文件

例如,如果您将文件存储在

Vec
中 - 并假设您有某种形式的间接寻址,例如
Vec<Box<File>>
(否则增长
Vec
将使所有现有文件无效)- 一切顺利。但编译器不知道这一点。

解决方案是使用一个特殊的集合,一个使用共享引用进行推送的集合。只要不允许删除,这样的集合就是合理的(不过,它可以通过可变引用允许删除)。并且可以使用不安全的代码编写。

幸运的是,您不需要编写不安全的代码 - 因为其他人已经这样做了!输入

elsa
板条箱,它提供各种仅追加的集合。

因此,假设您有

Vec<Box<File>>
,将其替换为
elsa::FrozenVec<Box<File>>
,将
&mut self
替换为
&self
,仅此而已!

© www.soinside.com 2019 - 2024. All rights reserved.