Pertimbangkan contoh mainan ini dari "Memerangi" dua "pemain" acak:

#[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!
}

Kode ini tidak akan berfungsi sebagai fungsi fight membutuhkan dua referensi yang bisa berubah menjadi elemen yang sama players vektor.

Solusinya jelek saya saat ini terlihat seperti berikut, menggunakan 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());

Saya ingin tahu apakah ada cara yang lebih efisien untuk melakukan ini untuk menghindari overhead ekstra RefCell? Bisakah saya memanfaatkan split_at_mut entah bagaimana?

0
t_d_milan 5 April 2021, 04:18

2 jawaban

Jawaban Terbaik

Anda dapat mengubah metode fight seperti berikutnya:

#[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);
}

Atau Anda dapat mencoba untuk menyelesaikan masalah ini dengan 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);
}

Atau jika Anda benar-benar membutuhkan kinerja 100%, Anda dapat mencoba menggali lebih dalam ke pointer mentah yang tidak aman. Tetapi Anda harus berpikir dua kali.

1
Dmitry 5 April 2021, 07:48

Dimungkinkan untuk menggunakan split_at_mut untuk meminjam keduanya secara eksklusif:

#[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 get2<T>(arr: &mut [T], a: usize, b: usize) -> (&mut T, &mut T) {
    use std::cmp::Ordering;

    let (sw, a, b) = match Ord::cmp(&a, &b) {
        Ordering::Less => (false, a, b),
        Ordering::Greater => (true, b, a),
        Ordering::Equal => panic!("attempted to exclusive-borrow one element twice"),
    };
    
    let (arr0, arr1) = arr.split_at_mut(a + 1);
    let (ea, eb) = (&mut arr0[a], &mut arr1[b - a + 1]);

    if sw {
        (eb, ea)
    } else {
        (ea, eb)
    }
}

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;
    
    let (p1, p2) = get2(&mut players, i1, i2);

    println!("{} ({} HP) vs {} ({} HP)", p1.attack, p1.health, p2.attack, p2.health);
    fight(p1, p2); // Error!
    println!("{} ({} HP) vs {} ({} HP)", p1.attack, p1.health, p2.attack, p2.health);
}
3
trentcl 5 April 2021, 13:57