问题描述
pub fn run_systems(mut self) {
for mut agent in self.agents.iter_mut() {
for mut system_id in agent.systems.iter_mut() {
let system = self.systems.get(system_id).unwrap();
system.simulate(&mut agent,&mut self);
}
}
}
cannot borrow `agent` as mutable more than once at a time
second mutable borrow occurs here`. Apparently,iterating over the arrays is a borrowing operation.
我也尝试过不使用 iter
函数并直接对拥有的值进行迭代(不确定那是做什么的):
pub fn run_systems(mut self) {
for mut agent in self.agents {
for mut system_id in agent.systems {
let system = self.systems.get(&system_id).unwrap();
system.simulate(&mut agent,&mut self);
}
}
}
但是一旦我引用 &system_id
,它就会检测到 for mut system_id in agent.systems {
行中的隐式借用
borrow of partially moved value: `agent`
partial move occurs because `agent.systems` has type `Vec<String>`,which does not implement the `copy` traitrustcE0382
lib.rs(72,34): `agent.systems` partially moved due to this implicit call to `.into_iter()`
collect.rs(233,18): this function takes ownership of the receiver `self`,which moves `agent.systems`
我尝试了各种方法来编写此代码,但找不到有效的方法。如何迭代这些值,同时还能够将对其内容的可变引用传递给其他函数?
use std::collections::HashMap;
struct World {
agents: Vec<Agent>,systems: HashMap<String,Box<dyn System>>,}
impl World {
pub fn new() -> World {
return World {
agents: Vec::new(),systems: HashMap::new(),};
}
pub fn run_systems(mut self) {
for mut agent in self.agents {
for system_id in agent.systems {
let system = self.systems.get(&system_id).unwrap();
system.simulate(&mut agent,&mut self);
}
}
}
/// Adds an agent to the world
pub fn add_agent(&mut self,agent: Agent) -> &Self {
self.agents.push(agent);
return self;
}
/// Adds a system to the available systems
pub fn add_system<S: System + 'static>(&mut self,system: S,id: String) -> &Self {
self.systems.insert(id,Box::new(system));
return self;
}
}
struct Agent {
systems: Vec<String>,}
trait System {
fn simulate(&self,agent: &mut Agent,world: &mut World);
}
#[derive(Default)]
struct SomeSystem;
impl System for SomeSystem {
fn simulate(&self,world: &mut World) {
// Code here
}
}
fn main() {
let system_id = String::from("SOME_SYstem");
let world = World::new();
world.add_system(SomeSystem::default(),system_id);
let agent = Agent {
systems: vec![system_id],};
world.add_agent(agent);
world.run_systems();
}
error[E0382]: borrow of partially moved value: `agent`
--> src/main.rs:18:33
|
16 | for system_id in agent.systems {
| -------------
| |
| `agent.systems` partially moved due to this implicit call to `.into_iter()`
| help: consider borrowing to avoid moving into the for loop: `&agent.systems`
17 | let system = self.systems.get(&system_id).unwrap();
18 | system.simulate(&mut agent,&mut self);
| ^^^^^^^^^^ value borrowed here after partial move
|
note: this function takes ownership of the receiver `self`,which moves `agent.systems`
= note: partial move occurs because `agent.systems` has type `Vec<String>`,which does not implement the `copy` trait
error[E0502]: cannot borrow `self` as mutable because it is also borrowed as immutable
--> src/main.rs:18:45
|
17 | let system = self.systems.get(&system_id).unwrap();
| ------------ immutable borrow occurs here
18 | system.simulate(&mut agent,&mut self);
| -------- ^^^^^^^^^ mutable borrow occurs here
| |
| immutable borrow later used by call
error[E0382]: borrow of partially moved value: `self`
--> src/main.rs:18:45
|
15 | for mut agent in self.agents {
| -----------
| |
| `self.agents` partially moved due to this implicit call to `.into_iter()`
| help: consider borrowing to avoid moving into the for loop: `&self.agents`
...
18 | system.simulate(&mut agent,&mut self);
| ^^^^^^^^^ value borrowed here after partial move
|
= note: partial move occurs because `self.agents` has type `Vec<Agent>`,which does not implement the `copy` trait
error[E0596]: cannot borrow `world` as mutable,as it is not declared as mutable
--> src/main.rs:54:5
|
53 | let world = World::new();
| ----- help: consider changing this to be mutable: `mut world`
54 | world.add_system(SomeSystem::default(),system_id);
| ^^^^^ cannot borrow as mutable
error[E0382]: use of moved value: `system_id`
--> src/main.rs:56:23
|
52 | let system_id = String::from("SOME_SYstem");
| --------- move occurs because `system_id` has type `String`,which does not implement the `copy` trait
53 | let world = World::new();
54 | world.add_system(SomeSystem::default(),system_id);
| --------- value moved here
55 | let agent = Agent {
56 | systems: vec![system_id],| ^^^^^^^^^ value used here after move
error[E0596]: cannot borrow `world` as mutable,as it is not declared as mutable
--> src/main.rs:58:5
|
53 | let world = World::new();
| ----- help: consider changing this to be mutable: `mut world`
...
58 | world.add_agent(agent);
| ^^^^^ cannot borrow as mutable
解决方法
我用来解决此类问题的一种方法是让 simulate
不接受可变引用,而是返回它希望执行的操作列表。这可能看起来像这样:
// No longer uses mutable references,and returns a list of
// actions to perform on the world.
trait System {
fn simulate(&self,agent: &Agent,world: &World) -> Vec<Action>;
}
enum Action {
...
}
impl World {
...
pub fn run_systems(&mut self) {
let mut actions = vec![];
for agent in &self.agents {
for system_id in &agent.systems {
let system = self.systems.get(system_id).unwrap();
actions.extend(system.simulate(agent,self));
}
}
for action in actions {
self.apply_action(action)
}
}
pub fn apply_action(&mut self,action: &Action) {
match(action) {
case SomeAction(agent_id) -> {
...
}
}
}
,
在任何一种情况下,这都不适用于 simulate()
的定义方式。它采用 &mut Self
作为第二个参数,这意味着对 self
的独占访问 - 所有 self
。但这是不可能的,因为 A) 在第一种情况下通过迭代器存在对 self
的引用,或者 B) 在第二种情况下通过取得 self
的所有权而部分解构了 self.agents
。
我会用我最终选择的解决方案来回答我自己的问题。
从根本上说,问题中的代码的问题在于,我们试图在对其进行迭代时对其进行变异(World 结构和代理列表)。 Rust 保护我们免受这种情况的影响,因为我们可能会在迭代过程中完全改变数组的大小或条目,这是您在使用其他语言编写代码时必须考虑的常见错误。
解决方案是根本不这样做,而是确保您不会迭代与您正在迭代的相同的东西。不过,我确实需要我的系统来改变代理状态,所以我所做的是将它们分成两个不同的部分:
// Agents have an id
pub struct Agent {
pub id: u128,pub systems: Vec<String>,}
// New AgentState separate struct
pub trait AgentState {
}
// The worrld contains a hashmap of states
struct World {
agents: Vec<Agent>,agent_states: HashMap<u128,Box<dyn AgentState>>,systems: HashMap<String,Box<dyn System>>,}
impl World {
// When adding a new agent,we also setup its state
pub fn add_agent<A: AgentState + 'static>(&mut self,state: A) -> &Self{
let agent = Agent::new(Vec::new());
let id = agent.id;
self.agents.push(agent);
self.agent_states.insert(id,Box::new(state));
self
}
// When running systems,we can pass a mutable reference to the states hashmap because we're not iterating on it
pub fn run_systems(&'static mut self) -> &Self {
for agent in self.agents.iter_mut() {
let systems = agent.systems.clone();
for system_id in systems {
let system = self.systems.get(&system_id).unwrap();
system.simulate(agent,&mut self.agent_states);
}
}
self
}
}
// The System trait signature now changes the simulate function to take the hashmap of states
/// The main trait that all system structs need to implement
pub trait System {
fn id() -> String where Self: Sized;
fn dyn_id(&self) -> String;
/// This function is called for every actor that uses the system and gives user code the opportunity to change the state
fn simulate<'a>(&self,agent: &'a mut Agent,states: &'a mut HashMap<u128,Box<dyn AgentState>>);
}
通过这种设置,当创建代理时,它们的状态是单独存储的,并且可以独立于代理迭代而改变。
对此有一个很大的警告,即您不能在系统内添加或删除代理状态,因为事情会变得不同步(代理将仍然存在,而其状态不会t)。对于此类更高级的用例,需要采用不同的方法。
只要不需要动态添加/删除代理,此解决方案就有效。