将对象的元素的可变引用作为函数的参数与该向量一起传递

时间:2017-01-28 10:58:19

标签: rust

我正在编写一个小游戏;我有一个怪物矢量,一个怪物可以对所有其他怪物造成伤害。我需要一个函数来获取所有怪物的矢量和对攻击怪物的引用。

我理解为什么对于Rust的类型系统来说这是不可能的:我需要对向量的可变引用和对怪物的可变引用,但这是不可能的,因为怪物属于向量。我找不到解决方法。

struct Monster {
    life: i32,
}

// give 1 damage to all except me
// increase my life by 1
fn give_1_damage(me: &mut Monster, all: &mut Vec<Monster>) {

    for m in all {
        m.life -= 1;
    }
    me.life += 2;
}

fn main() {
    let mut a = vec![Monster { life: 3 }, Monster { life: 3 }];
    let ref mut b = &mut a[0];
    give_1_damage(b, &mut a);
}

Lukas Kalbertodt proposes传递向量中怪物的偏移量。这很棒,谢谢!但实际上我的代码更复杂:

struct Monster {
    life: i32,
}

struct Game {
    player1: Vec<Monster>,
    player2: Vec<Monster>,
}

fn give_1_damage(player_idx: usize, monster_idx: usize, game: &mut Game) {
    // ...
}

我知道可以传递玩家的索引和怪物的索引,但我发现这很难看。我是否真的需要通过player_idxmonster_idx,而我以前知道哪个怪物在攻击,我可以参考它?

2 个答案:

答案 0 :(得分:2)

最简单的方法是传递索引而不是引用:

#[derive(Debug)]
struct Monster {
    life: i32,
}

// give 1 damage to all except me
// increase my life of 1
fn give_1_damage(monsters: &mut [Monster], me: usize) {
    for (i, m) in monsters.iter_mut().enumerate() {
        if i == me {
            m.life += 1;
        } else {
            m.life -= 1;
        }
    }
}

fn main() {
    let mut a = vec![Monster{life: 3}, Monster{life: 3}];
    println!("a: {:?}", a);
    let b = 0;
    give_1_damage(&mut a, b);
    println!("a: {:?}", a);
}

此外,由于您没有向怪物列表中添加任何内容,因此您应该使用&mut [Monster]而不是&mut Vec<Monster>;这种方式更通用。

答案 1 :(得分:2)

欢迎来到Rust!正如您已经注意到的那样,Rust类型系统(更具体地说:借用检查器)不会让您这样做。在Rust中,您必须以不同于您习惯的方式来思考某些问题。这实际上是一个好主意。

例如,让我们来看看你的代码:为了增加生命&#34;通过1,你在循环之外将它增加2,因为循环会将它减少1.这不是编写这样的代码的好方法,因为现在代码的不同部分是语义连接的,尽管它们不应该是#。是的。

那么你怎么能在Rust中做到这一点?

有多种方法可以做到这一点,但是这一点(评论中的解释):

#[derive(Debug)]
struct Monster {
    life: i32,
}

// Instead of passing a mutable reference, we just pass the index of the 
// attacking monster in the vector.
// Note that I also pass `&mut [...]` instead of `&mut Vec<...>`. This is
// sufficient as long as you only want to mutate single elements.
fn give_1_damage(me_idx: usize, all: &mut [Monster]) {
    // Here we split the vector in three parts: the part from the
    // beginning to the attacking monster, the attacking monster itself,
    // and the rest of the slice.
    let (front, me, back) = {
        // This is done by using those two special methods of slices.
        let (first, second) = all.split_at_mut(me_idx);
        let (me, rest) = second.split_first_mut().unwrap();
        (first, me, rest)
    };

    // Here we chain together the two parts and iterate over all monsters
    // except for the attacking one!
    for m in front.into_iter().chain(back) {
        m.life -= 1;
    }
    me.life += 1;
}

fn main() {
    let mut a = vec![Monster { life: 3 }, Monster { life: 3 }];
    give_1_damage(0, &mut a);
    println!("{:?}", a);
}

您可以尝试here