-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The lifetime of the borrowed value is too long #9113
Comments
cc @nikomatsakis who is probably best person to address this. I suspect it is indeed a similar problem to that of #6393 (and also #6613, which has a bit more explanation that is probably relevant to this example). |
FYI I did try to rewrite your example in a way that would pass the borrow-checker, but I was not successful. I did not go so far as to attempt to layer it all atop a single invocation of |
Interesting problem. The reason that
in place of your first |
@nikomatsakis One solution finally came to me today. Rather than try to generalize the borrow checker, we could instead generalize the hashmap API accordingly. In short, when in doubt, try Continuation-Passing-Style. :) Three step plan:
The gory details:
pub fn find_or_fallback_with<'a,A,T>(
&'a mut self,
k: K,
a: A,
found: &fn(&K, &'a mut V, A) -> T,
fallback: &fn(&'a mut HashMap<K, V>, &K, A) -> T) -> T; The crucial detail is that, upon failure, we pass along the
pub fn read3<'a>(&'a mut self, key: int) -> Option<&'a Data> {
self.cache.find_or_fallback_with(
key,
(),
// found:
|_key, data, _a| { let data : &'a Data = data; Some(data) },
// fallback:
|cache, _key, _a| {
match self.db.find(&key) {
Some(data) => {
let result: &Data = cache.find_or_insert(key, data.clone());
Some(result)
},
None => None
}
})
} (We might clean this up via
(But its a direct adaptation of the existing pub fn find_or_fallback_with<'a,A,T>(
&'a mut self,
k: K,
a: A,
found: &fn(&K, &'a mut V, A) -> T,
fallback: &fn(&'a mut HashMap<K, V>, &K, A) -> T) -> T {
if self.size >= self.resize_at {
// n.b.: We could also do this after searching, so
// that we do not resize if this call to insert is
// simply going to update a key in place. My sense
// though is that it's worse to have to search through
// buckets to find the right spot twice than to just
// resize in this corner case.
self.expand();
}
let hash = k.hash_keyed(self.k0, self.k1) as uint;
match self.bucket_for_key_with_hash(hash, &k) {
TableFull => fail!("Internal logic error"),
FoundEntry(idx) => {
found(&k, self.mut_value_for_bucket(idx), a)
}
FoundHole(_) => {
fallback(self, &k, a)
}
}
} (Obviously I did not need the accumulator |
My gut feeling is that this is not a problem of HashMap, but instead a problem of a borrow checker. CPS seems way less idiomatic, and this problem will occur in similar places (should all "containers" with find/insert include a CPS solution? Should I rewrite the function to use CPS if I need an extra caching layer?) Right now I'm using the extra contains_key check; while it's possible to fix HashMap to not require the check, I'd rather see the read2() work. For me borrow checks add enough extra burden in terms of thinking about the code that the better borrowck works the happier I am. |
In my opinion: this is the argument for the CPS solution. Complicating the borrow-checker rules would not solve the problem of it being hard to understand the reasoning of the borrow-checker. Conversely, the CPS solution (idiomatic or not), enables the borrow-checker (and thus the human being reasoning about the borrow checker), to explain the borrow checker's reasoning in terms of lexical scopes. (End of "In my opinion.") Having said all that, I'm pretty sure that @nikomatsakis and others have made the argument in the past that we do want to support idioms much like the one zeux described. So we will see. (My main goal was to prove to myself that one can still express the desired control flow here, given the appropriate library API.) |
From my perspective (take everything that I'm saying with a grain of salt, I'm very new to Rust) it's about simplifying borrowck behavior (even if it requires complicating borrowck logic). In the None arm, there is no accessible borrowed pointer that came from borrowing mut self. So for the user (me), it does not make much sense that the code does not compile. I would go so far as to say that in the Some arm, removing the usage of the value should let us borrow mut self again - since in some cases it will be important (i.e. if I'm returning Option<(&Chunk, float)>, but I'm only interested in the float part in Some branch - I want to be able to borrow mut self again. This is my opinion as a user; the call is obviously yours, but I hope the opinion is useful :) |
This is definitely a dup of #6393, going to close. |
Please fix the Borrow checker. Do not add new collections methods just to deal with this. The greatest benefit is that a simple match is easy to read and write. |
…logiq `new_without_default`: ignore const generics/lifetime params on `fn new` Fixes rust-lang#9113 No longer lints if `fn new` has any params changelog: [`new_without_default`]: no longer lints const generics and lifetime params on `fn new`
I'm not sure of the correct bug title here, it's based on my limited understanding of the problem :)
I'm trying to write a DB wrapper with a cache. Here's a code sample with two variants of read() function, the DB is using a hash map as well for exposition.
Both read1() and read2() fail to compile with the following error:
Possibly a duplicate of #6393 (although read1 looked like it could solve the problem but did not).
The text was updated successfully, but these errors were encountered: