简体   繁体   中英

Storing an iterator for a HashMap in a struct

Edit

As it seemms from the suggested solution, What I'm trying to achieve seems impossible/Not the correct way, therefore - I'll explain the end goal here:

I am parsing the values for Foo from a YAML file using serde, and I would like to let the user get one of those stored values from the yaml at a time, this is why I wanted to store an iterator in my struct


I have two struct similar to the following:

struct Bar {
    name: String,
    id: u32
}

struct Foo {
    my_map: HashMap<String, Bar>
}

In my Foo struct, I wish to store an iterator to my HashMap, so a user can borrow values from my map on demand. Theoretically, the full Foo class would look something like:

struct Foo {
    my_map: HashMap<String, Bar>,
    my_map_iter: HashMap<String, Bar>::iterator
}

impl Foo {
    fn get_pair(&self) -> Option<(String, Bar)> {
        // impl...
    }
}

But I can't seem to pull it off and create such a variable, no matter what I try (Various compilation errors which seems like I'm just trying to do that wrong).

I would be glad if someone can point me to the correct way to achieve that and if there is a better way to achieve what I'm trying to do - I would like to know that.

Thank you!

I am parsing the values for Foo from a YAML file using serde

When you parse them you should put the values in a Vec instead of a HashMap .

I imagine the values you have also have names which is why you thought a HashMap would be good. You could instead store them like so:

let parsed = vec![]

for _ in 0..n_to_parse {
    // first item of the tuple is the name second is the value
    let key_value = ("Get from", "serde");
    parsed.push(key_value);
}

then once you stored it like so it will be easy to get the pairs from it by keeping track of the current index:

struct ParsedHolder {
    parsed: Vec<(String, String)>,
    current_idx: usize,
}

impl ParsedHolder {
    fn new(parsed: Vec<(String, String)>) -> Self {
        ParsedHolder {
            parsed,
            current_idx: 0,
        }
    }

    fn get_pair(&mut self) -> Option<&(String, String)> {
        if let Some(pair) = self.parsed.get(self.current_idx) {
            self.current_idx += 1;
            Some(pair)

        } else {
            self.current_idx = 0;
            None
        }
    }
}

Now this could be further improved upon by using VecDeque which will allow you to efficiently take out the first element of parsed. Which will make it easy to not use clone. But this way you will be only able to go through all the parsed values once which I think is actually what you want in your use case.

But I'll let you implement VecDeque

The reason why this is a hard is that unless we make sure the HashMap isn't mutated while we iterate we could get into some trouble. To make sure the HashMap is immutable until the iterator lives:

use std::collections::HashMap;
use std::collections::hash_map::Iter;

struct Foo<'a> {
    my_map: &'a HashMap<u8, u8>,
    iterator: Iter<'a, u8, u8>,
}

fn main() {
    let my_map = HashMap::new();
    let iterator = my_map.iter();

    let f = Foo {
        my_map: &my_map,
        iterator: iterator,
    };
}

If you can make sure or know that the HashMap won't have new keys or keys removed from it (editing values with existing keys is fine) then you can do this:

struct Foo {
    my_map: HashMap<String, String>,
    current_idx: usize,
}

impl Foo {
    fn new(my_map: HashMap<String, String>) -> Self {
        Foo {
            my_map,
            current_idx: 0,
        }
    }

    fn get_pair(&mut self) -> Option<(&String, &String)> {
        if let Some(pair) = self.my_map.iter().skip(self.current_idx).next() {
            self.current_idx += 1;
            Some(pair)

        } else {
            self.current_idx = 0;
            None
        }
    }

    fn get_pair_cloned(&mut self) -> Option<(String, String)> {
        if let Some(pair) = self.my_map.iter().skip(self.current_idx).next() {
            self.current_idx += 1;
            Some((pair.0.clone(), pair.1.clone()))

        } else {
            self.current_idx = 0;
            None
        }
    }
}

This is fairly inefficient though because we need to iterate though the keys to find the next key each time.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM