How to update the existing todo item in Rust language - rust

Currently building a CRUD system and want to replace the selected item with the new updated item value. Since I am a noob, still need to learn lot of things, so how can I fix this. Completely confused on how to fix that.
What the current problem is that I am not able to find the item name for example bob and replace that with a new item value.
let action = std::env::args().nth(1).expect("Please provide an action");
let item = std::env::args().nth(2).expect("Please provide an item");
let _getitem = std::env::args().nth(3).expect("Please provide an item");
struct Todo {
map: HashMap<String, bool>,
}
if action == "edit" {
match todo.edit(&item, &_getitem) {
None => println!("'{}' is not present in the list", item),
Some(_) => match todo.save() {
Ok(_) => println!("todo saved"),
Err(why) => println!("An error occurred: {}", why),
},
}
}
fn edit(&mut self, key: &String, value: &String) -> Option<()> {
let elements = self.map.get_mut(key);
elements.push(value.to_string());
}
Data structure of hashmap looks like:
{"bob": true, "new": true }

I assume you want to update an existing record in a HashMap. One way to go about this is using HashMap::entry (Rust Doc):
Once you select an entry you can modify it using the Entry API. This allows you to chain updates and inserts, for example:
let mut map: HashMap<&'static str, usize> = HashMap::new();
map.insert("a", 1);
map
.entry("a")
.and_modify(|val| *val += 1);
In your case edit can look like this:
fn edit(&mut self, key: &String, value: bool) {
self.map.entry(key)
.and_modify(|val| *val = value);
}
Note that in your example the value of a HashMap must be bool but you are trying to update it to a string. You need to parse this first, e.g. using from_str (Rust Doc) or by using your own function:
fn to_bool(s: &str) -> Result<bool, ()> {
if s == "x" {
return Ok(true);
}
if s == "" {
return Ok(false);
}
Err(())
}

Related

How do I reduce multiple if branches to one in order to avoid duplicating similar logic?

A database API gives us the option to build a Filter object that will be passed onto a Query.
It offers a fluent API to build such Filter:
filter
.topic0(topic0)
.topic1(topic1)
.topic2(topic2)
.topic3(topic3)
The topics are pre-organised into a Vec<Option<String>>, which we can pass onto the filters obj, using the helper function:
fn add_topics(mut f: &Filter, topics: Vec<String>) {
if let Some(topic0) = topics.get(0) {
f = &f.topic0(topic0);
}
if let Some(topic1) = topics.get(1) {
f = &f.topic1(topic1);
}
if let Some(topic2) = topics.get(2) {
f = &f.topic2(topic2);
}
if let Some(topic3) = topics.get(3) {
f = &f.topic3(topic3);
}
}
Unfortunately, the db API doesn't offer a .topics() method that accepts a Vec<_>.
Still, is there any way to avoid duplication of the logic?
E.g. I'm proficient with JS/TS, in which the above can be written as:
const addTopics = (filter: Filter, topics: string[]) {
for (let i = 0; i < topics.length; ++i) {
if (topics[i]) {
filter = filter['topic' + i](topics[i]);
}
}
}
Rust, as a typed language, doesn't allow this as far as I know.
Is there any other way?
By a minimal reproducible example, Herohtar means something like:
struct Filter {}
impl Filter {
fn topic0(&self, topic: &str) -> &Self {
self
}
fn topic1(&self, topic: &str) -> &Self {
self
}
fn topic2(&self, topic: &str) -> &Self {
self
}
fn topic3(&self, topic: &str) -> &Self {
self
}
}
fn main() {
let topic0 = "";
let topic1 = "";
let topic2 = "";
let topic3 = "";
let filter = Filter {};
let f = filter
.topic0(topic0)
.topic1(topic1)
.topic2(topic2)
.topic3(topic3);
}
This abstracts away all of the details, while giving a clear example of what you mean. (If what I've written isn't what you mean, then that's the point of writing a minimal example.)
To build the function you're looking for, you need a way to map what method you want to call to each location. That's fairly straightforward, although it requires a somewhat advanced type:
fn add_topics(mut f: &Filter, topics: Vec<Option<String>>) {
// Fancy type because of the borrows that need a lifetime.
// It's just the signature for a method call. The `for` allows
// adding a lifetime.
type TopicAssigner = for<'a> fn(&'a Filter, &str) -> &'a Filter;
// Now, match up the methods in the order of the Vector
let assign_topic: Vec<TopicAssigner> = vec![
Filter::topic0,
Filter::topic1,
Filter::topic2,
Filter::topic3,
];
// And zip it with what you're passed
for (topic, assigner) in zip(topics, assign_topic) {
if let Some(topic) = topic {
f = (assigner)(f, &topic);
}
}
}
Note that this has a problem if topics has more values than are expected, so it would be nice to force it into a 4-element array instead:
fn add_topics(mut f: &Filter, topics: [Option<String>; 4]) {
But perhaps that's inconvenient for other reasons.

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.

How can I unwrap an Rc, obtained by Weak::upgrade, having more than one strong references?

I'm trying to implement an linked list for learning purposes and a feature I'm implementing now is fn pop_back which is to pop data in the last location in my linked list instance. RefCell, Rc and Weak are mainly used to store data into the list.
Here is my code (fn pop_back is at the bottom of the code) :
use std::cell::RefCell;
use std::rc::{Rc, Weak};
#[derive(Debug)]
pub struct DbNode<T> {
data: T,
next: Option<Rc<RefCell<DbNode<T>>>>,
prev: Option<Weak<RefCell<DbNode<T>>>>,
}
#[derive(Debug)]
pub struct DbList<T> {
first: Option<Rc<RefCell<DbNode<T>>>>,
last: Option<Weak<RefCell<DbNode<T>>>>,
}
impl<T> DbList<T> {
pub fn new() -> Self {
DbList {
first: None,
last: None,
}
}
pub fn push_front(&mut self, data: T) {
match self.first.take() {
Some(e) => {
let new_front = Rc::new(RefCell::new(DbNode {
data,
next: Some(e.clone()),
prev: None,
}));
let mut me = e.borrow_mut();
me.prev = Some(Rc::downgrade(&new_front));
self.first = Some(new_front);
},
None => {
let new_data = Rc::new(RefCell::new(DbNode {
data,
next: None,
prev: None,
}));
self.last = Some(Rc::downgrade(&new_data));
self.first = Some(new_data);
},
}
}
pub fn push_back(&mut self, data: T) {
match self.last.take() {
Some(l) => {
let new_back = Rc::new(RefCell::new(DbNode {
data,
next: None,
prev: Some(l.clone()),
}));
let st = Weak::upgrade(&l).unwrap();
let mut ml = st.borrow_mut();
self.last = Some(Rc::downgrade(&new_back));
ml.next = Some(new_back);
},
None => {
let new_data = Rc::new(RefCell::new(DbNode {
data,
next: None,
prev: None,
}));
self.last = Some(Rc::downgrade(&new_data));
self.first = Some(new_data);
},
}
}
pub fn pop_front(&mut self) -> Option<T> {
match self.first.take() {
Some(first) => {
match Rc::try_unwrap(first) {
Ok(refc) => {
let inner = refc.into_inner();
self.first = inner.next;
if let None = self.first {
self.last = None;
};
Some(inner.data)
},
Err(_) => None,
}
},
None => None,
}
}
pub fn pop_back(&mut self) -> Option<T> {
match self.last.take() {
Some(last) => {
// todo: try_unwrap goes to err: the 'prev' in the 'last' holding the reference of this?
match Rc::try_unwrap(Weak::upgrade(&last).unwrap()) {
Ok(refc) => {
let inner = refc.into_inner();
self.last = inner.prev;
Some(inner.data)
},
Err(_) => None,
}
},
None => None,
}
}
}
the fn pop_back should return the last DbNode if possible, and set the 'next' DbNode in the previous DbNode of the existing 'last' to the 'new last'. So I need to unwrap the existing to obtain the previous DbNode, which is going to be the new last. But Rc::try_unwrap goes to Err. I guess this is because the existing last itself is an Weak, so it already has one strong reference and the reference count increases with Rc::try_unwrap(Weak::upgrade(&last).unwrap()). I'm not 100% sure of my guess.
What should I do for it?
Completed
I've done this based on the approach suggested by #Masklinn. In addition to the approach, before Rc::try_unwrap, I just have to drop an Rc held by the 'first' field in DbList when the list has only one DbNode left, since the 'prev' field has None when only one DbNode is left in the list. It meets the same error I faced first, without dropping the 'first'.
Here is the final code on fn pop_back written:
pub fn pop_back(&mut self) -> Option<T> {
match self.last.take() {
Some(last) => {
let last = Weak::upgrade(&last).unwrap();
if Rc::ptr_eq(self.first.as_ref().unwrap(), &last) {
self.first = None;
} else {
let prev = Weak::upgrade(last.borrow().prev.as_ref().unwrap());
prev.as_ref().unwrap().borrow_mut().next = None;
self.last = Some(Rc::downgrade(prev.as_ref().unwrap()));
}
match Rc::try_unwrap(last) {
Ok(iv) => Some(iv.into_inner().data),
Err(_) => None,
}
},
None => None,
}
}
I guess this is because the existing last itself is an Weak, so it already has one strong reference and the reference count increases
This scheme of using weak references is really weird (that's not really what they're for), but here what you need to do is:
take from last()
upgrade last(), creating the second Rc
follow its prev() link to the second-to-last
set that as the new last
take from second-to-last's next, that's the first / existing Rc
drop either the upgraded value or the one you got from second-to-last, they're both strong references to the former last, this will leave you with a refcount of 1
which means you can unwrap it
You can't unwrap the Rc before the last step, because it will always have two strong refs before that.
Something along the lines of this:
pub fn pop_back(&mut self) -> Option<T> {
self.last.take().and_then(|last| {
// get a strong reference of last
let last = Weak::upgrade(&last).expect("Last element of the list was already deallocated.");
// get a strong reference of second-to-last
let prev = last.borrow().prev.as_ref().and_then(Weak::upgrade).expect("Previous-to-last element of the list was already deallocated");
// remove the old last from the new last
prev.borrow_mut().next = None;
// set second to last as the new last
self.last = Some(Rc::downgrade(&prev));
// extract the old last's payload and return it
Rc::try_unwrap(last).ok().map(|v| v.into_inner().data)
})
}
I swapped setting the new last and updating its next because that way all the things which must succeed (should only panic if the data structure is in an incoherent state) will run before we perform the first new mutation. Then we do all the mutation using in ways which should not be failable, so we should have no "panic hole".
I also just set its next to None directly, since we already have a strong reference on last we just care that the old one gets destroyed.

How can I set a struct field value by string name?

Out of habit from interpreted programming languages, I want to rewrite many values based on their key. I assumed that I would store all the information in the struct prepared for this project. So I started iterating:
struct Container {
x: String,
y: String,
z: String
}
impl Container {
// (...)
fn load_data(&self, data: &HashMap<String, String>) {
let valid_keys = vec_of_strings![ // It's simple vector with Strings
"x", "y", "z"
] ;
for key_name in &valid_keys {
if data.contains_key(key_name) {
self[key_name] = Some(data.get(key_name);
// It's invalid of course but
// I do not know how to write it correctly.
// For example, in PHP I would write it like this:
// $this[$key_name] = $data[$key_name];
}
}
}
// (...)
}
Maybe macros? I tried to use them. key_name is always interpreted as it is, I cannot get value of key_name instead.
How can I do this without repeating the code for each value?
With macros, I always advocate starting from the direct code, then seeing what duplication there is. In this case, we'd start with
fn load_data(&mut self, data: &HashMap<String, String>) {
if let Some(v) = data.get("x") {
self.x = v.clone();
}
if let Some(v) = data.get("y") {
self.y = v.clone();
}
if let Some(v) = data.get("z") {
self.z = v.clone();
}
}
Note the number of differences:
The struct must take &mut self.
It's inefficient to check if a value is there and then get it separately.
We need to clone the value because we only only have a reference.
We cannot store an Option in a String.
Once you have your code working, you can see how to abstract things. Always start by trying to use "lighter" abstractions (functions, traits, etc.). Only after exhausting that, I'd start bringing in macros. Let's start by using stringify
if let Some(v) = data.get(stringify!(x)) {
self.x = v.clone();
}
Then you can extract out a macro:
macro_rules! thing {
($this: ident, $data: ident, $($name: ident),+) => {
$(
if let Some(v) = $data.get(stringify!($name)) {
$this.$name = v.clone();
}
)+
};
}
impl Container {
fn load_data(&mut self, data: &HashMap<String, String>) {
thing!(self, data, x, y, z);
}
}
fn main() {
let mut c = Container::default();
let d: HashMap<_, _> = vec![("x".into(), "alpha".into())].into_iter().collect();
c.load_data(&d);
println!("{:?}", c);
}
Full disclosure: I don't think this is a good idea.

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!()
}

Resources