I have struct "M" which has member "children" which is a HashMap of more Ms. Ms full of other Ms make up the entire data structure. While traversing the data structure looking for specific values, I would like to collect a list of all the nodes that were passed through and return those to the caller. Ideally, these would be references to the existing Ms rather than me making copies of the data structure or portions of the data structure. The caller could then use more of M's methods on those returned items. Some of M's methods mutate, so that is a consideration.
I have code.
The Playground: Rust Playground
The Gist: Code shared from the Rust Playground · GitHub
use std::collections::HashMap;
// use std::rc::Rc;
// use std::cell::{RefCell, RefMut};
fn main() {
// This code simulates an insert() function.
let mut m = M { children: HashMap::new() };
let mut mb = M { children: HashMap::new() };
let mut mc: M = M { children: HashMap::new() };
let mut md: M = M { children: HashMap::new() };
let me: M = M { children: HashMap::new() };
md.children.insert("weld".to_string(), me);
mc.children.insert("nut".to_string(), md);
mb.children.insert("washer".to_string(), mc);
m.children.insert("bolt".to_string(), mb);
////////
// Prove to ourselves that the normal find() function works.
let a_success = m.find(vec!["bolt", "washer", "nut"]);
match a_success {
Ok(child) => println!("Successful success: {:?}", child),
Err(msg) => println!("Failed success: {}", msg)
}
let another_success = m.find(vec!["bolt", "washer", "nut", "weld"]);
match another_success {
Ok(child) => println!("Successful success: {:?}", child),
Err(msg) => println!("Failed success: {}", msg)
}
let a_failure = m.find(vec!["nut", "washer", "bolt"]);
match a_failure {
Ok(child) => println!("Failed failure: {:?}", child),
Err(msg) => println!("Successful failure: {}", msg)
}
let another_failure = m.find(vec!["weld"]);
match another_failure {
Ok(child) => println!("Failed failure: {:?}", child),
Err(msg) => println!("Successful failure: {}", msg)
}
// Desired usage pseudocode
// let mut m = M { children: HashMap::new() }
// m.insert(vec!["bolt", "washer"]);
// let blah = m.find_with_path(vec!["bolt", "washer"]);
// let new_m = blah.last(); // Washer M
// new_m.insert(vec!["nut", "weld"]);
// new_m.find(vec!["nut", "weld"]);
// m.find(vec!["bolt", "washer", "nut", "weld"]);
// Yay
// let another_m = blah[0]; // Bolt M
// another_m.find(vec!["washer"])
// Yay
// Profit
}
#[derive(Debug)]
struct M {
children: HashMap<String, M>
}
impl M {
fn find(&mut self, key: Vec<&str>) -> Result<&mut M, String> {
let mut current = Some(self);
for key_part in key[..].to_vec() {
match current.and_then(|item| item.children.get_mut(key_part)) {
Some(child) => current = Some(child),
None => current = None
}
}
match current {
Some(child) => Ok(child),
None => Err(format!("Unable to locate key [{}]", key.join(", ")))
}
}
fn find_with_path(&mut self, key: Vec<&str>) /* -> Some list of Ms */ {
let mut current = Some(self);
// let path: Vec<something> = Vec::new()
// ^^ Some structure to hold references to
// each M we successfully find, to be
// returned to caller
for key_part in key[..].to_vec() {
match current.and_then(|item| item.children.get_mut(key_part)) {
Some(child) => {
// Similar to other find function, but every time we
// successfully find another child M, we push it
// onto a list of some kind that can be returned to
// the caller. We then move onto the child and the
// next piece of the key, as in the other find
// function.
//
// Magic goes here. Somehow store reference in a
// list or other structure that will allow me to
// return a group of Ms or references to Ms to
// the caller. The caller must be able to get
// his hands on mutable versions of the Ms so
// as to call additional methods on them.
//
// I've tried various approaches. For a minute,
// I thought Rc/RefCell might save me, but I
// couldn't figure out how to store
// Rc<RefCell<&M>>s from current or child,
// because those belong to this function and/or
// this match arm.
current = Some(child)
},
None => current = None
}
}
}
}
There are comments in the code giving additional details about what I'm trying to achieve. I have left off the insert()
method. I have also written another find()
which uses recursion rather than the loop shown in this example.
Thanks!