Sololearn: Learn to Code
New course! Every coder should learn Generative AI!
Try a free lesson
+ 12
Sorry for being a little offtopic, I am not a Rust programmer, but I tremendously enjoy reading this thread. I wish most questions on SL were of similar quality... It would definitely motivate me to spend more time in the forum 😅 A fresh one on Rust's growing popularity: https://www.nature.com/articles/d41586-020-03382-2
2nd Dec 2020, 5:56 AM
Tibor Santa
Tibor Santa - avatar
+ 11
XXX I'm not sure if you stumbled onto this post on your own or if you're answering the call from my earlier post. Regardless, I'm glad you did as this has quickly become one of my favorite Q&A discussions in a long while. It's gotten me digging into the Rust documentation to fill in the gaps of this thread. It's been a long while since I've had to research something new like this. Coder Kitten I want to give you kudos for asking such a great question that has revealed some intricate aspects of this language that has further piqued my interest beyond my surface level awareness. It's the first time since delving into a language like Elixir that I've felt this intrigued about a language. This post might just be the point that nudged me over the edge to carve out time and learn this language. Thanks guys for driving such a great thread. 🙏
1st Dec 2020, 6:51 PM
David Carroll
David Carroll - avatar
+ 8
Oh... and by the way, Amazon just started a campaign to aggressively hirer Rust developers and are doubling down in their commitment to this language. I also know that Microsoft has taken a strong interest in Rust. This could be a solid language for people looking to accelerate their careers. https://www.zdnet.com/article/amazon-were-hiring-software-engineers-who-know-programming-language-rust/
1st Dec 2020, 6:53 PM
David Carroll
David Carroll - avatar
+ 8
Tibor Santa I completely agree with you. If you look at the Discuss tab in this app, this question is ranked 6th in Hot Today list. It's truly sad to see the other five Q&A Posts currently ranked higher than this one. Let's cherish this brief moment of a quality thread and hope it somehow leads to more like this one. *Edit:* This moved to 4th position. But the point is still the same. It's a lone sample of what could be.
2nd Dec 2020, 6:12 AM
David Carroll
David Carroll - avatar
+ 7
Coder Kitten [Part 1] Quoted from the book (https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value) "The or_insert method on Entry is defined to return a mutable reference to the value for the corresponding Entry key if that key exists, and if not, inserts the parameter as the new value for this key and returns a mutable reference to the new value. This technique is much cleaner than writing the logic ourselves and, in addition, plays more nicely with the borrow checker." std::collections::HashMap::entry - https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.entry std::collections::hash_map::Entry - https://doc.rust-lang.org/std/collections/hash_map/enum.Entry.html std::collections::hash_map::Entry::or_insert - https://doc.rust-lang.org/std/collections/hash_map/enum.Entry.html#method.or_insert
1st Dec 2020, 3:45 PM
XXX
XXX - avatar
+ 7
Coder Kitten [Part 2] For this to work however, you will need to change lines 6 and 16 from T: Fn(U) -> R to T: Fn(&U) -> R This is because we don't want the function to take the ownership of the key. I also believe that this will prevent problems in the long run, without actually affecting the code much. Taking references is much better than taking by value. Here is a working example https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5c3dd639b62bdc48b00ce2d21b3e4aaa
1st Dec 2020, 3:49 PM
XXX
XXX - avatar
+ 7
Coder Kitten [Part 2] The best solution IMO, would be using std::rc::Rc. You probably know about it if you have read the official book. The only downside is that it have a little bit of runtime cost because it allocates on the heap, but that would be very minute, as compared to using `Copy` or `Clone` (imagine cloning a 100 element vector of 100 element vectors). std::rc::Rc - https://doc.rust-lang.org/std/rc/struct.Rc.html Rc in the rust official book - https://doc.rust-lang.org/book/ch15-04-rc.html Working example https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=95b8c72a62a20249400d234aa1df0672 Although I still believe using Rc here would be unnecessary. Maybe Aaron Eberhardt can help. He knows Rust.
1st Dec 2020, 5:18 PM
XXX
XXX - avatar
2nd Dec 2020, 11:29 AM
Flash
+ 6
Coder Kitten [Part 1] Oh yes, my solution forgets the whole point of cache. But... are you sure the way you rewrote it works? It should give an error as on the line `self.results.insert(arg, r)` you are moving `arg` into the the scope of the `insert` method, which is why it is unusable on the line `self.results.get(&arg).unwrap()` I even checked https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6ae88c74b0e61b4449c2398222072c1a It might be working for you because you probably forgot to remove the constraint of the `Copy` trait on type parameters `R` and `U`, i.e. you might have forgotten to remove `+ Copy` on line 7 and 17, and `R: Copy` on line 8 and 18 (in the code mentioned in the question body).
1st Dec 2020, 5:03 PM
XXX
XXX - avatar
1st Dec 2020, 8:52 PM
Aaron Eberhardt
Aaron Eberhardt - avatar
+ 6
[Off-topic] David Carroll Tibor Santa I think that this one of the features of Rust I really like. It compels you to think about a problem from a perspective you probably wouldn't in other languages, which results in you coming out with an even better, optimized solution. In this case, garbage collected languages would simply add to the reference count of the variable and simple increase the validity scope of the variable, and others will maybe just pass out a copy of the value. But Rust is right now making us think how do we use values in different scopes without copying them or making their references dangling, and at the same time not suffer the runtime cost of a garbage collector.
2nd Dec 2020, 6:41 AM
XXX
XXX - avatar
+ 5
Anyone familiar enough with Rust to answer this question? Coder Kitten The linked code snippet is so pleasant to look at. This is the appeal that has so many experienced developers so intrigued about the Rust language. I would try to answer, but it wouldn't be based on knowledge gained from first hand experience with Rust. For this reason, I've posted this request to my network hoping it casts a wider reach on your behalf. 😉
1st Dec 2020, 2:02 PM
David Carroll
David Carroll - avatar
+ 5
Coder Kitten that's a very interesting question and to be honest I've never encountered such a hard ownership problem. First, the thing with the Copy trait is, that it implies that copying the data type is very efficient. That's great for small structs and primitive types but not for everything. The Clone trait however is pretty common and does not imply efficiency on cloning a data type. So you probably want to reduce the requirements from Copy to Clone to make it more general purpose. But of course that does not solve any ownership problems, it just makes it visible where the cloning (or copying) of an data type happens. The ownership problem boils down to this: The HashMap owns the keys and the values yet you need to return the value without a reference (because you can't return references to local variables). This makes it impossible to borrow which means you need to clone the data... Smart pointers won't help either but maybe RefCell or Cow could work, by enforcing borrow rules during runtime.
1st Dec 2020, 8:49 PM
Aaron Eberhardt
Aaron Eberhardt - avatar
+ 5
Coder Kitten I must admit I was partially wrong xD I just couldn't accept that Rust was unable to actually do this without copying the data every time which would be super inefficient so I though about it a little more. So here's what I came up with: The HashMap owns all the data, which makes sense. But this also means that you can't really borrow the data in this context because you could delete or replace the data later which would invalidate borrowed references which would be unsafe. This means, you can't eliminate the need to call clone somewhere because Rust won't allow you to move any data out of the HashMap. But you can at least use Rc, like XXX suggested to only clone a pointer, not the data. Rc guarantees Rust that there will always be a valid reference which solves the problem and even eliminates the Clone dependency for the R type. I think this could be the solution for your problem: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4f5da3122172a59f74a8b3edd7b0f668
1st Dec 2020, 9:19 PM
Aaron Eberhardt
Aaron Eberhardt - avatar
+ 5
(a bit off-topic) the reason don't have dive into Rust is that isn't here in SL yet. so why not write all an email with same title and send it to SL asking for include Rust. Just think about; lessons, assignments, challenges.. If someone with many followers, David Carroll for example, posts it, maybe enough people joins and SL team hears us.
1st Dec 2020, 11:34 PM
Kiwwi#
Kiwwi# - avatar
+ 5
Coder Kitten the problem you just mentioned can be solved by changing the function to pub fn value(&mut self, arg: U) -> &R { /* here you are not actually asking for a reference to value inside the HashMap, which would have happened if you called .get() Instead, you are calling .get() inside the `if` block, which is completely unrelated to the `else` block */ if self.results.contains_key(&arg) { self.results.get(&arg).unwrap() } else { let r = (self.f)(&arg); self.results.insert(arg, r); self.results.get(&arg).unwrap() // this is still an issue } } It solves the one-mutable-and-one-immutable-reference problem, but the other problem still persists. https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7bc4d4a1797e4d85cacb27de47811479
2nd Dec 2020, 6:27 AM
XXX
XXX - avatar
+ 4
Btw. there's a crate for caching function values called cached that also required the Clone trait to be implemented so they haven't found a solution either. Maybe you ask your question on reddit where more smart people tend to hang around.
1st Dec 2020, 8:51 PM
Aaron Eberhardt
Aaron Eberhardt - avatar
+ 3
Coder Kitten isn't that very similar to my first solution? Calling clone is usually fine but sometimes expansive, for example on a large Vec. So I think my second solution should perform much better on large data.
3rd Dec 2020, 4:32 PM
Aaron Eberhardt
Aaron Eberhardt - avatar