Storing an iterator for a HashMap in a struct - rust

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.

Related

impl push(self , item : T) for a struct with 2 Vecs<T>

I've been trying to impl the push for this struct:
struct StackMin<T: std::cmp::Ord>
{
stack : Vec<T>,
min : Vec<T>
}
like this:
fn push(&mut self, item: T) {
let l = self.stack.len();
let x: T;
match l {
0 => println!("There is nothing in the stack."),
n => {
if item <= self.stack[l - 1] {
self.stack.push(item); //item moved here
self.min.push(item); // so I can't use it again here
} else {
self.stack.push(item);
}
}
}
}
The problem is item moves with the first Vec<T>::push so I can't use it immediately at the second call of push(). I thought about making a variable let a = &item and use it in the second call, but push requires "T" and not "&T".
Also, if I try to do a=self.stack[l-1], it's an error because the T type doesn't have the Copy/Clone traits.
LATER EDIT: I also need to print the last value from the min Vector. But it doesn't have the std::fmt::Display , and I don't think it can be impl!? Any ideas?
How would you approach this?
Assuming you can change the inner values of the struct StackMin, but not the trait requirements, you could do something like this:
struct MinStack<T: std::cmp::Ord> {
// T is the data you want to store
// and usize points to the smallest T
inner: Vec<(T, usize)>
}
impl<T: std::cmp::Ord> MinStack<T> {
fn push(&mut self, val: T) {
let min_index = self.inner.last()
// get last min value and its index
.map(|(_, index)| (&self.inner[*index].0, index))
// check if it is smaller then the current value
.and_then(|(prev_min, min_index)|
(prev_min < &val).then(|| *min_index)
)
// if not smaller or does not exist
// set it to the current index
.unwrap_or(self.inner.len());
self.inner.push((val, min_index));
}
}
Here is a full implementation of the MinStack challenge Rust Playground.
Let me know if i should clarify something in the above code.
Docs for the used methods:
Vec::last
bool::then
Option::map
Option::and_then
Option::unwrap_or

What's the idiomatic way to create a iterator that owns some intermediate data and also points to it?

I'm trying to create a struct that wraps around stdin to provide something like C++'s std::cin.
I want to keep a String with the current line of the input and a SplitAsciiWhitespace iterator to its current token. When I reach the end of the iterator, I want to get a new line.
I'm not worried about error checking and I'm not interested in any crates. This is not for production code, it's just for practicing. I want to avoid using unsafe, as a way to practice the correct mindset.
The idea is that I can use it as follows:
let mut reader = Reader::new();
let x: i32 = reader.read();
let s: f32 = reader.read();
My current attempt is the following, but it doesn't compile. Can somebody give me a pointer on the proper way to do this?
struct Reader<'a> {
line: String,
token: std::str::SplitAsciiWhitespace<'a>,
}
impl<'a> Reader<'a> {
fn new() -> Self {
let line = String::new();
let token = line.split_ascii_whitespace();
Reader { line, token }
}
fn read<T: std::str::FromStr + std::default::Default>(&'a mut self) -> T {
let token = loop {
if let Some(token) = self.token.next() {
break token;
}
let stdin = io::stdin();
stdin.read_line(&mut self.line).unwrap();
self.token = self.line.split_ascii_whitespace();
};
token.parse().unwrap_or_default()
}
}
This question explains why it can't be done this way but does not provide an alternative solution. The "How do I fix it" section simply says "don't put these two things in the same struct", but I can't think of a way to do it separately while keeping a similar interface to the user.
Found a solution: keeping track of how much of the string we've read so far by using a simple index.
It does require some pointer arithmetic, but seems to work nicely.
Not sure if this counts as "idiomatic" Rust, tho.
struct Reader {
line: String,
offset: usize,
}
impl Reader {
fn new() -> Self {
Reader { line: String::new(), offset: 0 }
}
fn next<T: std::str::FromStr + std::default::Default> (&mut self) -> T {
loop {
let rem = &self.line[self.offset..];
let token = rem.split_whitespace().next();
if let Some(token) = token {
self.offset = token.as_ptr() as usize - self.line.as_ptr() as usize + token.len();
return token.parse::<T>().unwrap_or_default();
}
self.line.clear();
std::io::stdin().read_line(&mut self.line).unwrap();
self.offset = 0;
}
}
}

What's an idiomatic way to delete a value from HashMap if it is empty?

The following code works, but it doesn't look nice as the definition of is_empty is too far away from the usage.
fn remove(&mut self, index: I, primary_key: &Rc<K>) {
let is_empty;
{
let ks = self.data.get_mut(&index).unwrap();
ks.remove(primary_key);
is_empty = ks.is_empty();
}
// I have to wrap `ks` in an inner scope so that we can borrow `data` mutably.
if is_empty {
self.data.remove(&index);
}
}
Do we have some ways to drop the variables in condition before entering the if branches, e.g.
if {ks.is_empty()} {
self.data.remove(&index);
}
Whenever you have a double look-up of a key, you need to think Entry API.
With the entry API, you get a handle to a key-value pair and can:
read the key,
read/modify the value,
remove the entry entirely (getting the key and value back).
It's extremely powerful.
In this case:
use std::collections::HashMap;
use std::collections::hash_map::Entry;
fn remove(hm: &mut HashMap<i32, String>, index: i32) {
if let Entry::Occupied(o) = hm.entry(index) {
if o.get().is_empty() {
o.remove_entry();
}
}
}
fn main() {
let mut hm = HashMap::new();
hm.insert(1, String::from(""));
remove(&mut hm, 1);
println!("{:?}", hm);
}
I did this in the end:
match self.data.entry(index) {
Occupied(mut occupied) => {
let is_empty = {
let ks = occupied.get_mut();
ks.remove(primary_key);
ks.is_empty()
};
if is_empty {
occupied.remove();
}
},
Vacant(_) => unreachable!()
}

How do I automatically clear an attribute in a struct when it is moved?

I have a struct
struct Test {
list: Vec<u64>
}
and method in which I would like to get vector and erase list field to empty Vec
fn get_list(&self) -> Vec<u64> {
let list = Vec::new();
for item in self.list.drain() {
list.push(item);
}
list
}
It there another approach for doing it? Something like autoreinit field on moving value, for example:
fn get_list(&self) -> ???<Vec<u64>> {
self.list
}
Here is the solution, you can test on Rust playground (sadly share button doesn't work for me atm).
use std::mem;
#[derive(Debug)]
struct Test {
list: Vec<u64>
}
impl Test {
fn get_list(&mut self) -> Vec<u64> {
let repl = mem::replace(&mut self.list, Vec::new());
repl
}
}
fn main() {
let mut r = Test {
list : vec![1,2,3]
};
print!("r : {:?} ", r);
print!("replace : {:?} ", r.get_list());
print!("r : {:?} ", r);
}
You just need to run mem::replace(docs) on a mutable value and replace it with a value that will be moved in its place. In this case our destination is self.list and value we are replacing it is a blank Vec.
Things to note:
Field self.list of Test, needs to be taken as &mut self.list.
Previous change implies that self should be mutable as well.
Second parameter of replace is moved. That means it won't be available for further after this call. What this usually means, you either pass it a Vec constructor (e.g. Vec::new()) or clone of value that's replacing.
From #rust IRC
< theme> jiojiajiu, http://doc.rust-lang.org/nightly/std/mem/fn.replace.html

How to build a HashMap of Vectors in Rust?

I'm a Rust newbie. I'm trying to represent a directed graph's adjacency list as a HashMap of char {vertex name} to Vector of (char,int) {vertex name, cost}. I want the final HashMap to be immutable, but I'd like to build up the vector and then not need to make a copy of it to make it immutable.
My code is below. At the indicated line I get "cannot borrow immutable dereference (dereference is implicit, due to indexing) as mutable". This makes sense, as the Vec<(char,int)> in the map is not mutable. But I'm not sure how to fix it.
Is there a way to do this in Rust?
pub struct Edge {
to: char,
from: char,
weight: int
}
pub struct digraph {
_vertices: Vec<char>,
_adj_list: HashMap<char, Vec<(char,int)> >
}
impl digraph {
pub fn new(nodes: &Vec<char>, edges: &Vec<Edge> ) -> Option<digraph> {
let mut tmp_adj_list = HashMap::new();
for node in (*nodes).iter() {
tmp_adj_list.insert(*node, Vec::new());
}
for edge in (*edges).iter() {
let Edge{ to: to, from:from, weight:weight } = *edge;
if !(*nodes).contains(&to) | !(*nodes).contains(&from) {
return None;
}
tmp_adj_list[from].push((to,weight)) // *********** error here
}
Some(digraph { _vertices: (*nodes).clone(), _adj_list: tmp_adj_list })
}
}
Taking [] onto a HashMap is sugar for the (now deprecated) get(..) function, which declaration is :
fn get<'a>(&'a self, k: &K) -> &'a V
and returns a constant (&) reference. But the push(..) method of Vec expects a &mut reference, hence the error.
What you need is the get_mut(..) method of HashMap, which returns a &mut reference to the value.
Also, some minor points:
when calling a method, dereference is automatic : (*foo).bar() is exactly the same as foo.bar()
you can dereference automatically in your loop with for &edge in edges.iter() {...}
Including all this, your function becomes :
impl digraph {
pub fn new(nodes: &Vec<char>, edges: &Vec<Edge> ) -> Option<digraph> {
let mut tmp_adj_list = HashMap::new();
for &node in nodes.iter() {
tmp_adj_list.insert(node, Vec::new());
}
for &edge in edges.iter() {
let Edge{ to: to, from:from, weight:weight } = edge;
if !nodes.contains(&to) | !nodes.contains(&from) {
return None;
}
tmp_adj_list.get_mut(&from).push((to,weight))
}
Some(digraph { _vertices: nodes.clone(), _adj_list: tmp_adj_list })
}
}

Resources