【问题标题】:Pass mutable reference to an element of a vector as parameter of function alongside with that vector将向量元素的可变引用作为函数的参数与该向量一起传递
【发布时间】:2017-01-28 10:58:19
【问题描述】:

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

我明白为什么这在 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 而我之前知道哪个怪物正在攻击并且我可以参考它吗?

【问题讨论】:

标签: rust


【解决方案1】:

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

例如,让我们看一下您的代码:为了将“寿命”增加 1,您在循环外将其增加 2,因为循环会将其减少 1。这不是一个好方法编写这样的代码,因为现在代码的不同部分在语义上是相互关联的,尽管它们不应该如此。

那么如何在 Rust 中做到这一点?

有多种方法可以做到这一点,但这又如何(cmets 中的解释):

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

【讨论】:

    【解决方案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);
    }
    

    另外,既然你没有在怪物列表中添加任何东西,你应该选择&amp;mut [Monster]而不是&amp;mut Vec&lt;Monster&gt;;这样更通用。

    【讨论】:

      猜你喜欢
      • 2017-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-23
      • 2013-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多