use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap;
struct S {}
struct Foo {
counter: u64,
h: HashMap<u64, S>,
g: HashMap<String, u64>,
}
impl Foo {
fn add_h(&mut self) -> u64 {
self.counter += 1;
let s = S {};
self.h.insert(self.counter, s);
self.counter
}
fn add_g(&mut self, s: String) -> u64 {
match self.g.entry(s) {
Occupied(s) => *s.get(),
Vacant(v) => {
let h = self.add_h();
*v.insert(h)
}
}
}
fn add_g2(&mut self, s: String) -> u64 {
match self.g.entry(s) {
Occupied(s) => *s.get(),
Vacant(v) => {
let h = {
self.counter += 1;
let s = S {};
self.h.insert(self.counter, s);
self.counter
};
*v.insert(h)
}
}
}
}
The method add_g does not pass the borrow checker. It actually make sense, since I got self borrowed as mutable (by add_g) and then I try to borrow it again with add_h.
I can fix the problem with the method add_g2, that basically replace the call to add_h with the body of add_h itself (I made it explicit with a false scope). I don't like this approach since I am replicating logic that should not be replicated, IMHO.
I don't love it, because the function is actually more complex than the one showed here, so it is necessary to pass several parameters, but still better than replicating the code.
If those parameters are logically related, you could define a private struct containing just them, and have that struct as a field inside the main one.
Niko has a blog post about this issue. Three of the four ways around the problem that are discussed there (inlining, struct factoring, free variables) are already covered in this thread. The fourth way is "view structs", if you want to just jump to it.