Consider this toy example of "fighting" two random "players":
#[derive(Clone)]
struct Player {
name: String,
health: i32,
attack: i32,
}
fn fight(player_a: &mut Player, player_b: &mut Player) {
player_a.health -= player_b.attack;
player_b.health -= player_a.attack;
}
fn main() {
// Create Vector of 100 new players
let players: Vec<Player> = vec![
Player {
name: String::new(),
health: 100,
attack: 5,
};
100
];
// Pick two "random" indices
let i1 = 19;
let i2 = 30;
fight(&mut players[i1], &mut players[i2]); // Error!
}
This code will not work as the fight
function takes two mutable references to elements of the same players
vector.
My ugly workaround currently looks like the following, using RefCell
:
use std::cell::RefCell;
let mut players: Vec<RefCell<Player>> = vec![];
for _ in 0..100 {
players.push(RefCell::new(Player {
name: String::new(),
health: 100,
attack: 5,
}));
}
fight(&mut players[i1].borrow_mut(), &mut players[i2].borrow_mut());
I'd like to know if there's a more efficient way of doing this to avoid the extra overhead of RefCell
? Can I leverage split_at_mut
somehow?
You can change fight
method like next:
#[derive(Clone)]
struct Player {
name: String,
health: i32,
attack: i32,
}
fn fight(players: &mut [Player], player1_index: usize, player2_index: usize) {
players[player1_index].health -= players[player2_index].attack;
players[player2_index].health -= players[player1_index].attack;
}
fn main() {
// Create Vector of 100 new players
let mut players: Vec<Player> = vec![
Player {
name: String::new(),
health: 100,
attack: 5,
};
100
];
// Pick two "random" indices
let i1 = 19;
let i2 = 30;
fight(&mut players, i1, i2);
}
Or you can try to workaround this issue with Option
:
#[derive(Clone)]
struct Player {
name: String,
health: i32,
attack: i32,
}
fn fight(player_a: &mut Player, player_b: &mut Player) {
player_a.health -= player_b.attack;
player_b.health -= player_a.attack;
}
fn main() {
// Create Vector of 100 new players
let mut players: Vec<Option<Player>> = vec![
Some(Player {
name: String::new(),
health: 100,
attack: 5,
});
100
];
// Pick two "random" indices
let i1 = 19;
let i2 = 30;
let mut player1 = players[i1].take().unwrap();
let mut player2 = players[i2].take().unwrap();
fight(&mut player1, &mut player2);
players[i1].replace(player1);
players[i2].replace(player2);
}
Or if you really need 100% performance, you can try to dig deeper into unsafe raw pointers. But you should think twice.