2017-12-10 13 views
3

私は、言語を学ぶ方法として、今年の錆の大幕開けを試しています。私は、次のような構造に(7日目)からの入力を解析されました:どのようにしてRust HashMapを反復処理し、その値の一部を変更することができますか?

struct Process { 
    name: String, 
    weight: u32, 
    children: Vec<String>, 
    parent: Option<String> 
} 

これらはHashMap<String, Process>に格納されています。今度は、マップの値を繰り返し処理し、親の「子供」ベクトルにあるものに基づいて、親の値を更新したいと思います。動作しません何

は私がHashMapへの変更可能な参照(self.processes)と非可変の参照、または2つの可変の参照の両方を持つことはできません

for p in self.processes.values() { 
    for child_name in p.children { 
     let mut child = self.processes.get_mut(child_name).expect("Child not found."); 
     child.parent = p.name; 
    } 
} 

です。

だから、Rustでこれを達成するための最も慣用的な方法は何ですか?私が見ることができる2つのオプションがあります。

  1. 不変の参照がスコープ外になった後、第二のパスでプロセスの構造体を更新し、その後1回のパスで新しい一時データ構造に親/子関係をコピーし、。
  2. 自分のデータ構造を変更して、 "親"を独自のHashMapに配置します。

3番目のオプションはありますか?

+0

'self.processes'は' HashMap 'です。 – Stefan

+1

私はおそらく 'Vec 'に行き、 'usize'を他のエントリの参照として使用します。 2回目のパスのアプローチを使うことができました:最初に 'HashMap 'をビルドし、 'name' +' weight'を2回目のパスの '' children''と '' parent''に入れてください。多くの可能性:) – Stefan

+0

@Stefan:meh、私は不完全なコードで作業するのは好きではありません。私はそれが理由についての大丈夫な質問だと思う。 – ljedrz

答えて

4

はい、あなたはRefCellを使用してHashMapの値に内部可変性を付与することができます:子供はすでにborrowで借りている場合は、実行時にパニックになります

struct ProcessTree { 
    processes: HashMap<String, RefCell<Process>>, // change #1 
} 

impl ProcessTree { 
    fn update_parents(&self) { 
     for p in self.processes.values() { 
      let p = p.borrow();     // change #2 
      for child_name in &p.children { 
       let mut child = self.processes 
        .get(child_name)    // change #3 
        .expect("Child not found.") 
        .borrow_mut();     // change #4 
       child.parent = Some(p.name.clone()); 
      } 
     } 
    } 
} 

borrow_mut。これは、プロセスがそれ自身の親プロセスである場合に起こります(おそらく決して起こらないはずですが、より堅牢なプログラムでは、単にパニックするのではなく意味のあるエラーメッセージを与えたい)。

私はいくつかの名前を考案し、このコードをコンパイルするためにいくつかの小さな変更を加えました。特に、p.name.clone()p.nameのフルコピーを作成します。これは、nameparentの両方がStringであるために必要です。

関連する問題