0

Given that I know ParentId and ChildId, how would I find the UserId if the hashmap is:

HashMap<ParentId, HashMap<ChildId, HashMap<UserId, Foobar>>>

As my knowledge about Rust is pretty basic, I found that the following works but it's quite verbose, as I don't know any better:

match foobar.get(&pid) {
  Some(data1) => {
    println!("Found 1: {:?}", data1);

   match data1.get(&cid) {
     Some(data2) => {
      println!("Found 2: {:?}", data2);       
      
      ...and so on
     }, 
     _ => println!("Not found")     
   }
  },
  _ => println!("Not found")
}

I've also attempted chained get but it's tricky and did not find how to do it correctly:

foobar
        .get(pid)?
        .get(cid)?        
        .get(to_find)

What can I try next?

halfer
  • 19,824
  • 17
  • 99
  • 186
punkbit
  • 7,347
  • 10
  • 55
  • 89
  • What is your problem? Is the compiler erring? – Chayim Friedman Apr 25 '22 at 22:11
  • Note that we prefer a technical style of writing here. We gently discourage greetings, hope-you-can-helps, thanks, advance thanks, notes of appreciation, regards, kind regards, signatures, please-can-you-helps, chatty material and abbreviated txtspk, pleading, how long you've been stuck, voting advice, meta commentary, etc. Just explain your problem, and show what you've tried, what you expected, and what actually happened. – halfer May 16 '22 at 15:38

1 Answers1

1

You can use Option::and_then to chain operations that return Option<_>:

let _: Option<&Foobar> = foobar.get(&pid).and_then(|map| map.get(&cid)).and_then(|map| map.get(&to_find));

Example:

use std::collections::HashMap;

fn main() {
    let map: HashMap<i32, HashMap<bool, HashMap<String, bool>>> = HashMap::new();
    let _: Option<&bool> = map
        .get(&123)
        .and_then(|map| map.get(&true))
        .and_then(|map| map.get("foo"));
}

Playground


Your try with ? is also correct but it'll only work in a function that returns an Option as it returns None from the function the expression is in if any value is None, which is probably the error you're getting.

fn get(map: &HashMap<i32, HashMap<bool, HashMap<String, bool>>>) -> Option<bool> {
    map.get(&123)?.get(&true)?.get("foo").cloned()
}

Edit: As @Jmb pointed out in a comment below, another option is to create and immediately call a closure so you can use the ? operator which could be more readable in certain cases:

let _: Option<&bool> = (|| map.get(&123)?.get(&true)?.get("foo"))();
Dogbert
  • 212,659
  • 41
  • 396
  • 397
  • thanks a lot for your time and knowledge! I'll make sure I read about Option::and_then, as it seems easier to read than my attempt with `get`. – punkbit Apr 25 '22 at 22:21
  • There's also a proposal for [try blocks](https://doc.rust-lang.org/beta/unstable-book/language-features/try-blocks.html) which will let you do `let value = try { map.get(&123)?.get(&true)?.get("foo") };` but it's going to be a while before it's stabilized. – Dogbert Apr 25 '22 at 22:25
  • For readability, you can also use a crate like [`map_for`](https://crates.io/crates/map_for) (which I wrote before `?` made it into the language) or use an inner closure like in [this answer](https://stackoverflow.com/a/55756926/5397009). – Jmb Apr 26 '22 at 06:53
  • @Jmb thanks, I've used the closure approach multiple times but forgot to mention that. I've edited that into the answer! – Dogbert Apr 26 '22 at 07:58