Rust 的借用检查器真的意味着我应该重新构建我的程序吗?

Edw*_*ers 1 rust borrow-checker

所以我读过为什么我不能在同一结构中存储值和对该值的引用?我明白为什么我的天真方法不起作用,但我仍然很不清楚如何更好地处理我的情况。

我有一个程序,我想构造如下(省略细节,因为无论如何我都无法编译它):

use std::sync::Mutex;

struct Species{
    index : usize,
    population : Mutex<usize>
}
struct Simulation<'a>{
    species : Vec<Species>,
    grid : Vec<&'a Species>
}
impl<'a> Simulation<'a>{
    pub fn new() -> Self {...} //I don't think there's any way to implement this
    pub fn run(&self) {...}
}
Run Code Online (Sandbox Code Playgroud)

我的想法是,我创建一个向量Species(在 的生命周期内不会改变Simulation,除非在特定的互斥锁保护字段中),然后创建一个代表哪个物种生活在哪里的网格,它将自由改变。这个实现是行不通的,至少我想不出任何办法。据我了解,问题在于,无论我如何使用我的new方法,当它返回时,中的所有引用grid都会变得无效,因为Simulation,因此Simulation.species会被移动到堆栈中的另一个位置。即使我可以向编译器证明species及其内容将继续存在,它们实际上不会位于同一个位置。正确的?

我研究了解决这个问题的各种方法,例如在堆上制作或species使用Arcusize而不是引用,以及在物种向量中实现我自己的查找函数,但这些看起来更慢、更混乱或更糟糕。我开始思考的是,我需要真正重新构建我的代码,使其看起来像这样(详细信息用占位符填充,因为现在它实际运行):

use std::sync::Mutex;

struct Species{
    index : usize,
    population : Mutex<usize>
}
struct Simulation<'a>{
    species : &'a Vec<Species>, //Now just holds a reference rather than data
    grid : Vec<&'a Species>
}
impl<'a> Simulation<'a>{
    pub fn new(species : &'a Vec <Species>) -> Self { //has to be given pre-created species
        let grid = vec!(species.first().unwrap(); 10);
        Self{species, grid}
    }
    pub fn run(&self) {
        let mut population = self.grid[0].population.lock().unwrap();
        println!("Population: {}", population);
        *population += 1;
    }
}

pub fn top_level(){
    let species = vec![Species{index: 0, population : Mutex::new(0_)}];
    let simulation = Simulation::new(&species);
    simulation.run();
}
Run Code Online (Sandbox Code Playgroud)

据我所知,这运行良好,并勾选了所有理想的框:

  • grid对我来说使用简单的参考和最少的样板
  • 这些引用在编译时进行检查,系统开销最小
  • 安全性由编译器保证(与基于自定义映射的方法不同)

但是,这对我来说很奇怪:创建自有内存和引用的两步初始化过程无法以我所看到的任何方式进行抽象,这感觉就像我向调用函数公开了实现细节。top_level还必须负责建立任何其他函数或(作用域)线程来运行模拟、调用绘图/GUI 函数等。如果我需要多个级别的引用,我相信我将需要向该级别添加额外的初始化步骤。

所以,我的问题只是“我这样做对吗?”。虽然我不能完全证明这是错误的,但我觉得我失去了许多近乎通用的调用结构抽象。难道真的没有办法回去species吗?simulation在最后作为一对(通过一些一次性更新使所有引用都指向数据的“永远的家”)。

用第二种方式表达我的问题:我不喜欢我不能拥有签名为 的函数()-> Simulation,而我可以拥有一对具有相同效果的函数调用。我希望能够封装这个模拟的创建。我觉得这种方法不能这样做的事实表明我做错了什么,并且我可能缺少一种更惯用的方法。

Kev*_*eid 5

\n

我已经研究了解决这个问题的各种方法,例如将物种作为堆上的弧或使用 usizes 而不是引用以及在物种向量中实现我自己的查找函数,但这些看起来更慢、更混乱或更糟糕。

\n
\n

不要假设,测试一下。我曾经有一个与您非常相似的自引用(使用ouroboros)结构,其中包含事物向量和对它们的引用向量。我尝试重写它以使用索引而不是引用,而且速度更快

\n

Rc/也是一个值得尝试的选项 \xe2\x80\x94 请注意,当克隆或删除Arcan 时,引用计数只会产生额外的成本。取消引用的成本不会比 更高,并且您始终可以从 中获取an 。因此,只有当您更改元素中的哪个时,引用计数才重要ArcArc<Species>&Species&Species Arc<Species>SpeciesGrid

\n