2017-01-28 5 views
2

私は小さなゲームをコーディングしています。私にはモンスターのベクトルがあり、モンスターは他のすべてのモンスターにダメージを与えることができます。私はすべてのモンスターのベクトルと攻撃しているモンスターへの参照を取る関数が必要です。ベクトルの要素への参照を、そのベクトルと一緒に関数のパラメータとして渡します。

私は、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を実際に渡す必要がありますが、私は以前にどのモンスターが攻撃しているかを知っていましたが、私はそれを参照することができますか?

+0

あなたのフォローアップの質問は何の違いもなく、あなたはLukasやDKと同じ機能を呼び出すことができます。実際に配列であれば 'give_1_damage(0、&mut game.player1);または' give_1_damage(0、&mut game.players [player_idx]); – Shepmaster

+0

関連していないので、[なぜ文字列(&String)かVec(&Vec)への参照を関数の引数として受け入れることをお勧めしませんか?](http://stackoverflow.com/q/40006219/155423) – Shepmaster

答えて

2

Rust!あなたがすでに気づいたように、Rustタイプのシステム(具体的には、貸し出しチェッカー)はあなたにそれをさせません。錆では、あなたが慣れ親しんでいたのとは異なる方法で特定の問題について考える必要があります。これは実際には良いアイデアです。

たとえば、 "life"を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それを試すことができます。

2

インデックスではなく参照渡しすることです最も簡単な方法:あなたはモンスターのリストには何も追加していないことから、あなたは&mut [Monster]ではなく&mut Vec<Monster>を取る必要があり、また

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

を。それはより一般的な方法です。

関連する問題