Hi,
I've hit this problem a few times since developping in Rust, and I was wondering if I'm doing something wrong or if there's something I'm not understanding properly.
This is not actual code from a project, but simply represents a pattern I have to write from time to time:
struct Test {
v: Vec<(String, u32)>,
}
impl Test {
fn find_or_add(&mut self, key: String, value: u32) -> &u32 {
let r = self.v.iter().find(|(k, _)| **k==key);
if let Some ((_, v)) = r {
return v;
}
drop(r);
self.v.push((key, value));
&self.v.last().unwrap().1
}
}
The borrow checker complains about "self.v.push".
It says "self.v" cannot be borrowed as mutable, because it's already borrowed immutably by iter above.
Since the loop either returns or completes, the code under it should be able to borrow self.v again!
I even added the drop(r) to drop any reference that might linger.
The way I understand the borrow checker, past the drop(r) call, it should be like a clean slate, nothing
borrowed.
What am I missing ?
(As you can see from the test example, it's a get_or_add() pattern, but I just wrote this quickly to demonstrate the issue I'm having. Actual code differs slightly, but the principle is the same)
That drop() cannot possibly matter, because references are Copy. If anything, borrowck could figure out that the borrow ended from the control flow (return).
IIRC this code will be accepted by the next-generation borrow checker, Polonius.
Yeah like I said, that drop doesn't have any meaning, I just put it there for emphasis.
I still don't understand why it won't compile though. Usually the borrow checker is pretty good
at detecting when variables are used, but it this case I don't get it.
If I write this, which is functionally the same, but doesn't look as good.. It works fine:
fn find_or_add_loop(&mut self, key: String, value: u32) -> &u32 {
for i in 0..self.v.len() {
if self.v[i].0==key {
return &self.v[i].1;
}
}
self.v.push((key, value));
&self.v.last().unwrap().1
}