Access another element immutably while mutating an element of a HashMap - rust

I'm working on a game that involves a bunch of Beetle objects stored in a HashMap. Each beetle has a position, and it can also have a target id which is the key for another beetle in the hash. If a beetle has a target, it needs to move toward the target each time the game loop executes.
I can't perform the lookup of the target's current position, because you can't have a mutable and immutable borrow at the same. I get that, but any ideas how to restructure for my specific case?
I think I'm just getting caught up in how easy this would be in pretty much any other language, I can't see the idiomatic Rust way to do it. Here's a pretty minimal but complete example:
use std::collections::HashMap;
type Beetles = HashMap<i32, Beetle>;
struct Beetle {
x: f32,
y: f32,
target_id: i32,
}
impl Beetle {
fn new() -> Beetle {
Beetle {
x: 0.0,
y: 0.0,
target_id: -1,
}
}
}
fn main() {
let mut beetles: Beetles = HashMap::new();
beetles.insert(0, Beetle::new());
beetles.insert(1, Beetle::new());
set_target(&mut beetles, 0, 1);
move_toward_target(&mut beetles, 0);
}
fn set_target(beetles: &mut Beetles, subject_id: i32, target_id: i32) {
if let Some(subject) = beetles.get_mut(&subject_id) {
subject.target_id = target_id;
}
}
fn move_toward_target(beetles: &mut Beetles, beetle_id: i32) {
if let Some(subject) = beetles.get_mut(&beetle_id) {
if let Some(target) = beetles.get(&subject.target_id) {
// update subject position to move closer to target...
}
}
}

You could solve your specific problem by performing a double lookup for the subject. First, borrow immutably from the hash map to collect the information necessary for updating the subject. Then finally update the subject using the collected information by borrowing mutably from the hash map:
fn move_toward_target(beetles: &mut Beetles, beetle_id: i32) {
if let Some(subject_target_id) = beetles.get(&beetle_id).map(|b| b.target_id) {
let mut target_xy = None; // example
if let Some(target) = beetles.get(&subject_target_id) {
// collect information about target relevant for updating subject
target_xy = Some((target.x, target.y)) // example
}
let subject = beetles.get_mut(&beetle_id).unwrap();
// update subject using collected information about target
if let Some((target_x, target_y)) = target_xy{ // example
subject.x = target_x;
subject.y = target_y;
}
}
}
However, it is likely that you will run in similar and more complex problems with your beetles in the future, because the beetles are your central game objects, which you will likely want to reference mutably and immutably at the same time at several places in your code.
Therefore, it makes sense to wrap your beetles in std::cell::RefCells, which check borrow rules dynamically at runtime. This gives you a lot flexibility when referencing beetles in your hash map:
fn main() {
let mut beetles: Beetles = HashMap::new();
beetles.insert(0, RefCell::new(Beetle::new()));
beetles.insert(1, RefCell::new(Beetle::new()));
set_target(&mut beetles, 0, 1);
move_toward_target(&mut beetles, 0);
}
fn set_target(beetles: &mut Beetles, subject_id: i32, target_id: i32) {
if let Some(mut subject) = beetles.get_mut(&subject_id).map(|b| b.borrow_mut()) {
subject.target_id = target_id;
}
}
fn move_toward_target(beetles: &mut Beetles, beetle_id: i32) {
if let Some(mut subject) = beetles.get(&beetle_id).map(|b| b.borrow_mut()) {
if let Some(target) = beetles.get(&subject.target_id).map(|b| b.borrow()) {
//example for updating subject based on target
subject.x = target.x;
subject.y = target.y;
}
}
}
updated Beetles type:
type Beetles = HashMap<i32, RefCell<Beetle>>;

Related

Hashmap multiple mutable borrow issue after reference drop

I am trying to pass around a HashMap which stores values through a set of nested enums/structs. The problem of multiple mutability happens during iteration, even all references should be dropped.
The general idea is to have a vector of values, iterate through them and simplify them, keeping track of them within the HashMap. There are two stages of simplification.
The general flow looks something like
run(Vec<ComplexVal>)
-for each val->
val.fix_complex(holder)
-for each `smp` SimpleVal in val->
basicval = Simplifier::step(smp, holder)
holder.insert("name", basicval)
But the problem is that the holder is borrowed mutably in each stage, and there isn't supposed to be any reference from the ComplexVal to the holder and since the borrowchecker doesn't like multiple borrows, it fails.
Full playground snippet: here
It happens in this snippet:
pub fn run(&mut self, mut vals: Vec<ComplexVal>) {
let mut holder = Holder{hold:HashMap::new()};
// .. setup holder code omitted
let len = vals.len();
for _ in 0..len {
let mut val = vals.remove(0); // remove from vec, should drop after running
println!("Running {:?}", val);
match val {
ComplexVal::Cmplx1(mut c) => {
c.fix_complex(&mut holder)
},
//... more cases of different types of values omitted for simplicity
}
// val *should* be dropped here, and therefore the mutable borrow of holder?
}
println!("Holder: {:?}", holder);
}
}
The only thing I can think of is that it somehow is related to the BasicVal::Ref(&BasicVal) value when created.
I need to return a reference of type &BasicVal so I can't use a regular fn() -> &BasicVal as the reference would be dangling, so I pass a ret value which is to be modified and used as the storage for the return value.
I have also tried just returning the enum BasicVal::Ref(&BasicVal), but run into the same mutability issues.
The example below is a much more simple version which (sort of) demonstrates the same error, just thought I'd include this context in case someone has another idea on how to implement this which wouldn't have these issues
Code (edited)
Updated playground link
Edit: I made a mistake in not needing the lifetimes of both holder and ret to explicitly be the same, so I have made an updated example for it
use std::borrow::BorrowMut;
///////////////////////////////
use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
#[derive(Debug)]
enum BasicVal<'a> {
Ref(&'a BasicVal<'a>),
Val1(BasicStruct),
}
#[derive(Debug)]
struct Holder<'b> {
hold: HashMap<String, RefCell<BasicVal<'b>>>,
}
#[derive(Debug)]
struct BasicStruct {
val: i32,
}
impl<'a> BasicVal<'a> {
pub fn empty() -> Self { BasicVal::Val1(BasicStruct { val: 0 }) }
}
// must match sig of modify_val_ref
fn modify_val<'f>(holder: &'f mut Holder<'f>, mut ret: RefMut<BasicVal<'f>>) {
*ret = BasicVal::Val1(BasicStruct { val: 5 });
}
// must match sig of modify_val
fn modify_val_ref<'f>(holder: &'f mut Holder<'f>, mut ret: RefMut<BasicVal<'f>>) {
ret = holder.hold.get("reference_val").unwrap().borrow_mut();
}
fn do_modify<'f>(holder: &'f mut Holder<'f>) {
let mut v = RefCell::new(BasicVal::empty());
println!("Original {:?}", v);
modify_val(holder, v.borrow_mut());
holder.hold.insert("Data".to_string(), v);
println!("Modified {:?}", holder.hold.get("Data"));
}
pub fn test_dropborrow() {
let mut holder = Holder { hold: HashMap::new() };
holder.hold.insert(
"reference_val".to_string(),
RefCell::new(BasicVal::Val1(BasicStruct { val: 8 })),
);
do_modify(&mut holder);
}
pub fn main() {
test_dropborrow();
}
Edit: Using just the holder for a temp return value gives me a multiple mutable borrow issue, so that workaround doesn't work. I have also tried it with a RefCell with the same issue.
fn modify_val<'f>(holder: &'f mut Holder<'f>) {
holder.hold.insert("$return".to_string(), BasicVal::Val1(BasicStruct{val: 5}));
}
fn do_modify<'f>(holder: &'f mut Holder<'f>) {
modify_val(holder);
let mut v = holder.hold.remove("$return").unwrap();
holder.hold.insert("Data".to_string(), v);
println!("Modified {:?}", v);
}
Error:
935 | fn do_modify<'f>(holder: &'f mut Holder<'f>) {
| -- lifetime `'f` defined here
936 |
937 | modify_val(holder);
| ------------------
| | |
| | first mutable borrow occurs here
| argument requires that `*holder` is borrowed for `'f`
938 | let mut v = holder.hold.remove("$return").unwrap();
| ^^^^^^^^^^^ second mutable borrow occurs here
Any help is greatly appreciated!!!
Figured it out, essentially the BasicVal<'a> was causing Holder to mutably borrow itself in successive iterations of the loop, so removing the lifetime was pretty much the only solution

How can I apply mutating method calls to each struct when using Iter::find? [duplicate]

This question already has answers here:
How do I mutate the item in Iterator::find's closure?
(2 answers)
Closed 1 year ago.
My code is below, and also on the playground.
use rand::Rng;
const THRESHOLD: i32 = 50;
#[derive(Debug)]
struct Game {
plays: Vec<i32>
}
impl Game {
fn new() -> Self {
Self {
plays: vec![]
}
}
/// A game wins when the sum of all plays exceeds the threshold
fn play(&mut self, play: i32) -> bool {
self.plays.push(play);
self.plays.iter().sum::<i32>() > THRESHOLD
}
}
fn main() {
// Build the games
let mut games: Vec<Game> = Vec::new();
let mut rng = rand::thread_rng();
for _ in 1..10 {
games.push(Game::new());
}
// Play the games & find a winner
loop {
if let Some(winner) = games
.iter_mut()
.find(|game| {
let play = rng.gen_range(1..=10);
game.play(play)
}) {
println!("Winner!: {:?}", winner);
}
}
}
The compiler doesn't like game.play(play) inside the predicate given to find saying:
`game` is a `&` reference, so the data it refers to cannot be borrowed as mutable
My attempts to dereference game have only further offended the borrow checker. What is the idiomatic way to call a mutating method inside a find predicate?
You can use Iterator::find_map instead, whose closure takes in elements by value which can then be easily mutated:
games
.iter_mut()
.find_map(|game| {
let play = rng.gen_range(1..=10);
game.play(play).then(|| game)
})
Playground

Access Impl field from closure before field is alloc'ed in Rust?

I am new to Rust, as will probably be obvious.
Basically I have this scenario you can see below where, I create a new type that has a closure added to it, but this closure needs to access data which has not yet been created. The data will be created by the time the closure gets called, but when the closure is initially created the data is not yet available.
What is the best way to do deal with?
I am also curious if my closure was not a closure, but rather a private function in my implementation, how would I access that data? This closure/function is a callback from WasmTime and requires an explicit method signature which does not allow me to add $self to it. So how could I get at the instance fields of the implementation without a reference to $self in the function parameters?
pub struct EmWasmNode {
wasmStore: Store<WasiCtx>,
wasmTable: Table,
}
impl EmWasmNode {
pub fn new(filePath: &str) -> Result<Self> {
let engine = Engine::default();
// let module = Module::from_file(&engine, "wasm/index.wast")?;
let module = Module::from_file(&engine, filePath)?;
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker(&mut linker, |s| s)?;
let wasi = WasiCtxBuilder::new()
.inherit_stdio()
.inherit_args()?
.build();
let mut store = Store::new(&engine, wasi);
linker.func_wrap("env", "emscripten_set_main_loop", |p0: i32, p1: i32, p2: i32| {
println!("emscripten_set_main_loop {} {} {}", p0, p1, p2);
/*** How would I access wasmTable and wasmStore from here to execute more methods??? ***/
//let browserIterationFuncOption:Option<wasmtime::Val> = Self::wasmTable.get(&mut Self::wasmStore, p0 as u32);
// browserIterationFuncOption.unwrap().unwrap_funcref().call(&store, ());
})?;
let instance = linker.instantiate(&mut store, &module)?;
let table = instance
.get_export(&mut store, "__indirect_function_table")
.as_ref()
.and_then(extern_table)
.cloned();
let start = instance.get_typed_func::<(), (), _>(&mut store, "_start")?;
start.call(&mut store, ())?;
Ok(EmWasmNode {
wasmStore: store,
wasmTable: table.unwrap(),
})
}
You have to instantiate a struct before. I suggest the more simple code below to see my idea.
struct Atype
{
name: String,
}
impl Atype
{
pub fn new() -> Self
{
Self{ name: String::from("zeppi")}
}
pub fn test(&self) -> ()
{
let func = | x | { println!("{} {}", &self.name, x);};
func(3)
}
}
fn main() {
let o = Atype::new();
o.test();
}

Rust: concurrency error, program hangs after first thread

I have created a simplified version of my problem below, I have a Bag struct and Item struct. I want to spawn 10 threads that execute item_action method from Bag on each item in an item_list, and print a statement if both item's attributes are in the bag's attributes.
use std::sync::{Mutex,Arc};
use std::thread;
#[derive(Clone, Debug)]
struct Bag{
attributes: Arc<Mutex<Vec<usize>>>
}
impl Bag {
fn new(n: usize) -> Self {
let mut v = Vec::with_capacity(n);
for _ in 0..n {
v.push(0);
}
Bag{
attributes:Arc::new(Mutex::new(v)),
}
}
fn item_action(&self, item_attr1: usize, item_attr2: usize) -> Result<(),()> {
if self.attributes.lock().unwrap().contains(&item_attr1) ||
self.attributes.lock().unwrap().contains(&item_attr2) {
println!("Item attributes {} and {} are in Bag attribute list!", item_attr1, item_attr2);
Ok(())
} else {
Err(())
}
}
}
#[derive(Clone, Debug)]
struct Item{
item_attr1: usize,
item_attr2: usize,
}
impl Item{
pub fn new(item_attr1: usize, item_attr2: usize) -> Self {
Item{
item_attr1: item_attr1,
item_attr2: item_attr2
}
}
}
fn main() {
let mut item_list: Vec<Item> = Vec::new();
for i in 0..10 {
item_list.push(Item::new(i, (i+1)%10));
}
let bag: Bag= Bag::new(10); //create 10 attributes
let mut handles = Vec::with_capacity(10);
for x in 0..10 {
let bag2 = bag.clone();
let item_list2= item_list.clone();
handles.push(
thread::spawn(move || {
bag2.item_action(item_list2[x].item_attr1, item_list2[x].item_attr2);
})
)
}
for h in handles {
println!("Here");
h.join().unwrap();
}
}
When running, I only got one line, and the program just stops there without returning.
Item attributes 0 and 1 are in Bag attribute list!
May I know what went wrong? Please see code in Playground
Updated:
With suggestion from #loganfsmyth, the program can return now... but still only prints 1 line as above. I expect it to print 10 because my item_list has 10 items. Not sure if my thread logic is correct.
I have added println!("Here"); when calling join all threads. And I can see Here is printed 10 times, just not the actual log from item_action
I believe this is because Rust is not running your
if self.attributes.lock().unwrap().contains(&item_attr1) ||
self.attributes.lock().unwrap().contains(&item_attr2) {
expression in the order you expect. The evaluation order of subexpressions in Rust is currently undefined. What appears to be happening is that you essentially end up with
const condition = {
let lock1 = self.attributes.lock().unwrap();
let lock2 = self.attributes.lock().unwrap();
lock1.contains(&item_attr1) || lock2.contains(&item_attr2)
};
if condition {
which is causing your code to deadlock.
You should instead write:
let attributes = self.attributes.lock().unwrap();
if attributes.contains(&item_attr1) ||
attributes.contains(&item_attr2) {
so that there is only one lock.
Your code would also work as-is if you used an RwLock or ReentrantMutex instead of a Mutex since those allow the same thread to have multiple immutable references to the data.

How can I return the combination of two borrowed RefCells?

I have a struct with two Vecs wrapped in RefCells. I want to have a method on that struct that combines the two vectors and returns them as a new RefCell or RefMut:
use std::cell::{RefCell, RefMut};
struct World {
positions: RefCell<Vec<Option<Position>>>,
velocities: RefCell<Vec<Option<Velocity>>>,
}
type Position = i32;
type Velocity = i32;
impl World {
pub fn new() -> World {
World {
positions: RefCell::new(vec![Some(1), None, Some(2)]),
velocities: RefCell::new(vec![None, None, Some(1)]),
}
}
pub fn get_pos_vel(&self) -> RefMut<Vec<(Position, Velocity)>> {
let mut poses = self.positions.borrow_mut();
let mut vels = self.velocities.borrow_mut();
poses
.iter_mut()
.zip(vels.iter_mut())
.filter(|(e1, e2)| e1.is_some() && e2.is_some())
.map(|(e1, e2)| (e1.unwrap(), e2.unwrap()))
.for_each(|elem| println!("{:?}", elem));
}
}
fn main() {
let world = World::new();
world.get_pos_vel();
}
How would I return the zipped contents of the vectors as a new RefCell? Is that possible?
I know there is RefMut::map() and I tried to nest two calls to map, but didn't succeed with that.
You want to be able to modify the positions and velocities. If these have to be stored in two separate RefCells, what about side-stepping the problem and using a callback to do the modification?
use std::cell::RefCell;
struct World {
positions: RefCell<Vec<Option<Position>>>,
velocities: RefCell<Vec<Option<Velocity>>>,
}
type Position = i32;
type Velocity = i32;
impl World {
pub fn new() -> World {
World {
positions: RefCell::new(vec![Some(1), None, Some(2)]),
velocities: RefCell::new(vec![None, None, Some(1)]),
}
}
pub fn modify_pos_vel<F: FnMut(&mut Position, &mut Velocity)>(&self, mut f: F) {
let mut poses = self.positions.borrow_mut();
let mut vels = self.velocities.borrow_mut();
poses
.iter_mut()
.zip(vels.iter_mut())
.filter_map(|pair| match pair {
(Some(e1), Some(e2)) => Some((e1, e2)),
_ => None,
})
.for_each(|pair| f(pair.0, pair.1))
}
}
fn main() {
let world = World::new();
world.modify_pos_vel(|position, velocity| {
// Some modification goes here, for example:
*position += *velocity;
});
}
If you want to return a new Vec, then you don't need to wrap it in RefMut or RefCell:
Based on your code with filter and map
pub fn get_pos_vel(&self) -> Vec<(Position, Velocity)> {
let mut poses = self.positions.borrow_mut();
let mut vels = self.velocities.borrow_mut();
poses.iter_mut()
.zip(vels.iter_mut())
.filter(|(e1, e2)| e1.is_some() && e2.is_some())
.map(|(e1, e2)| (e1.unwrap(), e2.unwrap()))
.collect()
}
Alternative with filter_map
poses.iter_mut()
.zip(vels.iter_mut())
.filter_map(|pair| match pair {
(Some(e1), Some(e2)) => Some((*e1, *e2)),
_ => None,
})
.collect()
You can wrap it in RefCell with RefCell::new, if you really want to, but I would leave it up to the user of the function to wrap it in whatever they need.

Resources