0
\$\begingroup\$

I'm working on a card game using Amethyst's Specs crate and am fairly new to Rust (therefore I'm learning through the fight with the compiler). My first System is to deal the cards to each player. I have:

  • a Card type that has Copy semantics
  • a Cards component that represents a set of Card the players or deck are holding
struct Deal {
 deck: Entity,
 players: Vec<Entity>,
}
impl<'a> System<'a> for Deal {
 type SystemData = WriteStorage<'a, Cards>;
 fn run(&mut self, mut cards: Self::SystemData) {
 // code smell - can't use `get_mut`
 let mut deck = cards.get(self.deck).unwrap().clone();
 deck.shuffle();
 for player in self.players.iter().cycle() {
 let player_cards = cards.get_mut(*player).unwrap();
 if let Some(card) = deck.take_top() {
 player_cards.add(card);
 } else {
 break;
 }
 }
 // code smell
 let mut deck = cards.get_mut(self.deck).unwrap();
 deck.clear(); 
 }
}

The compiler complains that I can't take a mutable reference to the deck's cards because the player_cards is a mutable one (or vice versa).

As noted in the code above, I clone the Cards for the deck, shuffle that, deal to the mutable players' and then empty the deck.

I would have expected that I could get a mutable reference to the deck up front, shuffle that and remove it.

Is there something simple I am missing?

Aside, I do not know if it is convention to pass entities on the system struct it self or not. Is that wrong?

asked Aug 2, 2021 at 12:58
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I'm not familiar with ECS or some of the types you used in your code, and to be honest I don't think I quite understand your code as it seems incomplete, but this seems like something that could be fixed by using the Rc and RefCell types. Instead of having cards as having type Self::SystemData, I think you'd want it to be Rc<RefCell<Self::SystemData>> instead, which should allow you to create multiple mutable references to it by using the Rc::clone function.

Whilst using Rc and RefCell, you'd still need to make sure that you stick to Rust's borrowing semantics though, as this will then be runtime-checked instead of compile-time checked. This section of the book has more information about these types and how they might be used.

I don't have your full code, so I can't check if this is correct, but I think you should end up with something similar to:

use std::{cell::RefCell, rc::Rc};
struct Deal {
 deck: Entity,
 players: Vec<Entity>,
}
impl<'a> System<'a> for Deal {
 type SystemData = WriteStorage<'a, Cards>;
 fn run(&mut self, mut cards: Rc<RefCell<Self::SystemData>>) {
 let mut deck = Rc::clone(&cards).get(self.deck).unwrap().clone();
 deck.shuffle();
 for player in self.players.iter().cycle() {
 let player_cards = Rc::clone(&cards).get_mut(*player).unwrap();
 if let Some(card) = deck.take_top() {
 player_cards.add(card);
 } else {
 break;
 }
 }
 let mut deck = Rc::clone(&cards).get(self.deck).unwrap().clone();
 deck.clear(); 
 }
}

After switching to using Rc and RefCell in some cases, you might even find that you're able to do without using explicit lifetime annotations, but YMMV.

answered Oct 9, 2021 at 10:21
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.