rust - 对于基于代理的模拟,如何以高效的方式规避“不能一次多次借用 `*self` 作为可变变量”?
问题描述
我想在 Rust 中实现基于代理的模拟,但我遇到了借用检查器。
代理应该生活在一个可变网格中,在每个单元格中携带一个状态。每个代理都带有一些可变状态。通常,我会实现一组代理,例如HashMap
从网格位置到代理。在模拟步骤中,我将遍历所有代理,然后根据代理自身的状态、该位置的网格状态以及附近其他代理的状态更新代理的状态。
虚构的示例可能如下所示:
use std::collections::HashMap;
struct Agent { // each agent carries some state
id: i64,
state: i32,
}
struct CellState { // some state of a grid cell
state: i64,
}
struct Chart {
agents: HashMap<usize, Agent>,
grid: Vec<CellState>,
}
impl Chart {
fn new(size: usize) -> Chart {
let mut agents = HashMap::new(); // generate hash and populate with 2 agents
agents.insert(10, Agent { id: 1, state: 1 });
agents.insert(11, Agent { id: 2, state: 0 });
let mut grid: Vec<CellState> = Vec::with_capacity(size);
Chart {
agents: agents,
grid: grid,
}
}
fn do_stuff(&mut self, agent: &mut Agent) {
// here we want to update the state of agent,
// based on the state of other agents in the grid
}
fn step_agents(&mut self) {
for (_, agent) in &mut self.agents {
self.do_stuff(agent);
}
}
}
fn main() {
let mut ch = Chart::new(128);
ch.step_agents();
}
此代码产生错误
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/main.rs:37:13
|
36 | for (_, agent) in &mut self.agents {
| ----------------
| |
| first mutable borrow occurs here
| first borrow later used here
37 | self.do_stuff(agent);
| ^^^^ second mutable borrow occurs here
我理解错误以及 Rust 编译器出现问题的原因。我不明白的是如何以高效的方式规避这个问题。
如果我一成不变地借用对代理的引用,我就无法更新它的状态。在一个真实的例子中,代理会携带相当多的状态,因此克隆并不便宜。
实现这一点的惯用 Rust 方式是什么?
解决方案
拥有多个独占引用,在这种情况下指向同一个HashMap
,确实违反了借用检查器的约束。
鉴于Agent
复制显然并不便宜,我认为您可能希望考虑包装Agent
以std::cell::RefCell
动态借用值。
这是一个简单的例子:
use std::cell::RefCell;
use std::collections::HashMap;
#[derive(Debug, PartialEq)]
struct Agent {
id: i64,
state: i32,
}
struct CellState {
state: i64,
}
struct Chart {
agents: HashMap<usize, RefCell<Agent>>,
grid: Vec<CellState>,
}
impl Chart {
fn new(size: usize) -> Self {
let mut agents = HashMap::new();
agents.insert(1, RefCell::new(Agent { id: 1, state: 0 }));
agents.insert(2, RefCell::new(Agent { id: 2, state: 1 }));
let mut grid: Vec<CellState> = Vec::with_capacity(size);
Self { agents, grid }
}
fn do_stuff(&self, agent: &RefCell<Agent>) {
for other in self.agents.values().filter(|&other| agent != other) {
if other.borrow().state == 1 {
agent.borrow_mut().state += 1;
}
}
}
fn step_agents(&self) {
for agent in self.agents.values() {
self.do_stuff(agent);
}
}
}
fn main() {
let mut chart = Chart::new(128);
chart.step_agents();
for agent in chart.agents.values() {
println!("{:?}", agent);
}
}
由于HashMap
以任意顺序访问,上面的内容可以返回:
RefCell { value: Agent { id: 1, state: 1 } }
RefCell { value: Agent { id: 2, state: 1 } }
推荐阅读
- javascript - 如何向 actions.order.create paypal 集成添加更多属性
- reactjs - React setState 在提交输入表单后不更新状态
- azure-maps - Azure Maps - 检测缩放方向
- xamarin.forms - 如何在 xamarin Forms 中监控蓝牙设备连接状态?
- javascript - 如何转到下一张幻灯片 jQuery
- android - 向上滚动时显示工具栏 - 具有多个捕捉点的 CoordinatorLayout
- php - 默认显示一个 symfony 表单块,但我需要零个块
- java - 如果可以通过反射绕过访问修饰符,它们的目的是什么?
- azure-devops-rest-api - 使用 powershell 脚本获取测试套件的默认测试人员
- r - 在 R 中将数字 1000 格式化为 1k,将 1000000 格式化为 1m 等