[英]Access two mutable references from a Global hashmap at once in Rust
Say we have a globally accessible hashmap of trait objects we make with lazy_static : MY_ANIMALS: Mutex<HashMap<i32, AnimalBox>>
, where type AnimalBox = Box<dyn AnimalExt+Send>
假设我们有一个全局可访问的 trait 对象哈希图,我们使用lazy_static : MY_ANIMALS: Mutex<HashMap<i32, AnimalBox>>
,其中type AnimalBox = Box<dyn AnimalExt+Send>
Now, we want the animals in this global hashmap to interact with each other.现在,我们希望这个全局哈希图中的动物能够相互交互。 For example, one AnimalBox
can AnimalExt::eat(&mut self, prey: &mut AnimalBox)
another.例如,一个AnimalBox
可以AnimalExt::eat(&mut self, prey: &mut AnimalBox)
另一个。
The problem is that our eat()
function requires both a mutable reference to self, as well as a mutable reference to the pray (because we want the pray to AnimalExt::perish(&mut self)
when it's eaten.问题是我们的eat()
函数既需要一个对self 的可变引用,也需要一个对pray 的可变引用(因为我们希望在它被吃掉时向AnimalExt::perish(&mut self)
发送AnimalExt::perish(&mut self)
。
However, getting two mutable references to our hashmap causes a WouldBlock
Error:但是,获得对我们的哈希图的两个可变引用会导致WouldBlock
错误:
use lazy_static::lazy_static;
use std::sync::Mutex;
use std::collections::HashMap;
//type alias for our boxed animals
type AnimalBox = Box<dyn AnimalExt+Send>;
//globally accessible hashmap for keeping track of our animals throughout the scope of our application
lazy_static! {
static ref MY_ANIMALS: Mutex<HashMap<i32, AnimalBox>> = Mutex::new(HashMap::new());
}
//simple trait for our animals
trait AnimalExt{
//eat() function requires a mutable reference to another AnimalBox
fn eat(&mut self, pray: &mut AnimalBox);
fn perish(&mut self);
fn energy(&self)->i32;
fn id(&self)->i32;
}
struct Wolf{
id: i32,
energy: i32,
alive: bool,
}
impl AnimalExt for Wolf{
fn id(&self)->i32{
self.id
}
fn eat(&mut self, pray: &mut AnimalBox) {
pray.perish();
self.energy+= pray.energy()
}
fn energy(&self) ->i32 {
self.energy
}
fn perish(&mut self){
self.alive = false;
}
}
impl Wolf{
pub fn new(id: i32)->Self{
Wolf{
id: id,
energy: 50,
alive: true,
}
}
}
struct Cow{
id: i32,
energy: i32,
alive: bool,
}
impl Cow{
pub fn new(id: i32)->Self{
Cow{
id: id,
energy: 100,
alive: true,
}
}
}
impl AnimalExt for Cow{
fn id(&self)->i32{
self.id
}
fn eat(&mut self, pray: &mut AnimalBox) {
pray.perish();
self.energy+= pray.energy()
}
fn energy(&self) ->i32 {
self.energy
}
fn perish(&mut self){
self.alive = false;
}
}
fn main() {
println!("Hello, world!");
//define our animals
let cow1 = Box::new(Cow::new(1)) as AnimalBox;
let cow2 = Box::new(Cow::new(2)) as AnimalBox;
let wolf1 = Box::new(Wolf::new(3)) as AnimalBox;
let wolf2 = Box::new(Wolf::new(4)) as AnimalBox;
//insert them into the global hashmap
MY_ANIMALS.lock().unwrap().insert(cow1.id(), cow1);
MY_ANIMALS.lock().unwrap().insert(cow2.id(), cow2);
MY_ANIMALS.lock().unwrap().insert(wolf1.id(), wolf1);
MY_ANIMALS.lock().unwrap().insert(wolf2.id(), wolf2);
//getting one animal to eat() another causes a WouldBlock error
match (MY_ANIMALS.try_lock().unwrap().get_mut(&0), MY_ANIMALS.try_lock().unwrap().get_mut(&1)){
(Some(a1), Some(a2))=>{
a1.eat(a2);
}
_=>()
}
}
Is there a good work around for this?有没有好的解决方法? Or is there no safe way to do this with a hashmap?或者有没有安全的方法可以用哈希图来做到这一点? I've seen this answer to a similar question, but the suggested answer suggests using RefCell
, which is incompatible with lazy_static's Send
trait requirement.我已经看到了类似问题的这个答案,但建议的答案建议使用RefCell
,这与lazy_static 的Send
特征要求不兼容。
I ended up using the muti_mut crate, which provides several methods for having multiple mutable references to a HashMap or BTreeMap.我最终使用了muti_mut crate,它提供了多种方法来对 HashMap 或 BTreeMap 进行多个可变引用。
use lazy_static::lazy_static;
use std::sync::Mutex;
use std::collections::HashMap;
use multi_mut::{HashMapMultiMut, HashMapMutWrapper};
//type alias for our boxed animals
type AnimalBox = Box<dyn AnimalExt+Send>;
//globally accessible Hashmap for keeping track of our animals throughout the scope of our application
lazy_static! {
static ref MY_ANIMALS: Mutex<HashMap<i32, AnimalBox>> = Mutex::new(HashMap::new());
}
//simple trait
trait AnimalExt{
//eat() function requires a mutable reference to another AnimalBox
fn eat(&mut self, pray: &mut AnimalBox);
fn perish(&mut self);
fn energy(&self)->i32;
fn id(&self)->i32;
}
struct Wolf{
id: i32,
energy: i32,
alive: bool,
}
impl AnimalExt for Wolf{
fn id(&self)->i32{
self.id
}
fn eat(&mut self, pray: &mut AnimalBox) {
pray.perish();
self.energy+= pray.energy()
}
fn energy(&self) ->i32 {
self.energy
}
fn perish(&mut self){
self.alive = false;
}
}
impl Wolf{
pub fn new(id: i32)->Self{
Wolf{
id: id,
energy: 50,
alive: true,
}
}
}
struct Cow{
id: i32,
energy: i32,
alive: bool,
}
impl Cow{
pub fn new(id: i32)->Self{
Cow{
id: id,
energy: 100,
alive: true,
}
}
}
impl AnimalExt for Cow{
fn id(&self)->i32{
self.id
}
fn eat(&mut self, pray: &mut AnimalBox) {
pray.perish();
self.energy+= pray.energy()
}
fn energy(&self) ->i32 {
self.energy
}
fn perish(&mut self){
self.alive = false;
}
}
fn main() {
println!("Hello, world!");
//define our animals
let cow1 = Box::new(Cow::new(1)) as AnimalBox;
let wolf1 = Box::new(Wolf::new(2)) as AnimalBox;
let before_eating_cow = wolf1.energy();
//insert them into the global hashmap
MY_ANIMALS.lock().unwrap().insert(cow1.id(), cow1);
MY_ANIMALS.lock().unwrap().insert(wolf1.id(), wolf1);
//use get_pair_mut method from the multi_mut crate
match MY_ANIMALS.try_lock().unwrap().get_pair_mut(&1, &2){
Some((hunter, prey))=>{
dbg!("hunter eats prey");
hunter.eat(prey);
}
None=>()
}
let after_eating_cow = MY_ANIMALS.lock().unwrap().get(&1).unwrap().energy();
assert_ne!(before_eating_cow, after_eating_cow);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.