2016-11-29 34 views
7

構造体に別の型への参照であるメンバ変数を与えるのに問題があります。ここに私の構造体および実装は次のとおりです。ベクトル内の要素への参照

struct Player<'a> { 
    current_cell: &'a Cell, 
} 

impl<'a> Player<'a> { 
    pub fn new(starting_cell: &'a Cell) -> Player<'a> { 
     Player { current_cell: starting_cell } 
    } 
} 

プレイヤーは、彼らがしている現在のCellへの参照を持ってここに私のGame構造体とその実装されています。

struct Game { 
    is_running: bool, 
    cells: Vec<Cell>, 
} 

impl Game { 
    pub fn new() -> Game { 
     let cells = construct_cells(); 
     let player = Player::new(cells[0]); 

     Game { 
      is_running: false, 
      cells: cells, 
     } 
    } 
} 

cellsCellのベクトルでありますs。ゲームを作成すると、construct_cells()にセルのベクトルを作成し、最初のセルでプレイヤーを開始します。私は取得していますエラーは次のとおりです。

expected &Cell, found struct `Cell` 

私はPlayerを作成するとき、私は参照を渡していないよいることがわかりますが、私は&cells[0]にパラメータを変更した場合、それは全体のベクトルを借りるために私に叫ぶと、私はGame構造体を作成するときに再びそれを使用しようとしています。どうしたの?プレーヤーにCellへの参照を与えるにはどうすればよいですか?

+0

リファレンス(またはポインタ)が何であるか、特にダングリングリファレンス*(またはポインタ)は何ですか?もしあなたがあなたの住所(123 Main St.)を持っている友人に一枚の紙を送ってから**あなたが**移ったなら、どうなるでしょうか?あなたの友人がちょうど現れて、あなたのように住んでいた家の人々と話し始めると、家の新しいオーナーはどのように感じますか? – Shepmaster

+0

@Shepmaster私はぶら下がっているポインタが何であるか知っています。私はこの解決策が1つを可能にすることを理解できなかったと思う。同じ結果を得るにはどうすればいいですか?私は何をしようとしていますか? – Dooskington

+1

*私は、この解決策が1つを可能にすることを理解していませんでした。 - それがRustがすばらしい言語であるIMOです。ローカル変数から 'Cell'を' Game'構造体に移動すると、 'player'に与えたようなベクトルへの参照を無効にしました。 CやC++のような言語はAFAIKを実行させ、実行時にコードをクラッシュさせます。 'player'は決して使われないので、あなたが本当にやりたいことを言うのは難しいです。 – Shepmaster

答えて

10

外見にもかかわらず、可変ベクトルに格納されたオブジェクトへの参照を格納することは、ではなく、で安全です。ベクターは増殖できることを覚えておいてくださいベクトルがその容量を超えると、大きな配列を割り当ててのすべてのオブジェクトを新しい場所に移動することによってベクトルが拡張されます。ベクトル内のオブジェクトへの参照はすべてぶら下がり、Rustはそれが起こらないようにします。 (また、ベクトルを縮小または消去することができます。その場合、その要素への参照は、明らかに解放されたメモリを指し示します)。同じ問題がC++ std::vectorに存在します。

周囲にはいくつかの方法があります。

struct Player<'a> { 
    game: &'a Game, 
    cell_idx: usize, 
} 

impl<'a> Player<'a> { 
    pub fn new(game: &'a Game, cell_idx: usize) -> Player<'a> { 
     Player { 
      game: game, 
      cell_idx: cell_idx, 
     } 
    } 
    pub fn current_cell_name(&self) -> &str { 
     &self.game.cells[self.cell_idx].name 
    } 
} 

完全コンパイル例はat the playgroundある:一つはGameへのバックポインタおよびベクトル要素のインデックスで構成されて安全な後方参照にCellへの直接参照を切り替えることです。

上記は、不正行為のように見えるかもしれません。結局のところ、Rustは参照を持つシステム言語であり、インデックスを使用することはややこしいものです。私たちはもっとうまくできますか?

もう1つの選択肢は、Cellオブジェクトがベクトルの再割り当ての影響を受けないことを確認するために少しの労力を費やすことです。 C++では1は、ポインタ・ツー・セルの代わりに細胞のベクトルのベクトルを使用することによってこれを達成するであろうし、錆に我々はボックス使用して同じ操作を行うことができますのコストで

struct Game { 
    is_running: bool, 
    cells: Vec<Box<Cell>>, 
} 

impl Game { 
    fn construct_cells() -> Vec<Box<Cell>> { 
     ["a", "b", "c"].iter() 
      .map(|n| Box::new(Cell { name: n.to_string() })) 
      .collect() 
    } 

    pub fn new() -> Game { 
     let cells = Game::construct_cells(); 

     Game { 
      is_running: false, 
      cells: cells, 
     } 
    } 
} 

を各Cellための追加割当(及びGameから各セルへの追加ポインタ参照)、これはPlayerの最大限効率的な実装を可能にする:

struct Player<'a> { 
    cell: &'a Cell, 
} 

impl<'a> Player<'a> { 
    pub fn new(cell: &'a Cell) -> Player<'a> { 
     Player { 
      cell: cell, 
     } 
    } 
    pub fn current_cell_name(&self) -> &str { 
     &self.cell.name 
    } 
} 

再び、完全コンパイル例at the playground

関連する問題