I am trying to implement a simple bucketed hash table in Rust (just for practice). The hash table struct is defined as:
pub struct BucketedHashTable<K: Hash, V> {
buckets: Vec<Bucket<K, V>>,
size: usize,
}
where Bucket
is my simple implementation of a singly linked stack.
In pretty much every method of the table (put
, remove
, get
) I will be getting the bucket where the key is to be inserted into (removed from, looked up in), so I extracted a method for just that:
fn pick_bucket(&mut self, key: K) -> &mut Bucket<K, V> {
let mut hasher = DefaultHasher::new();
key.hash(&mut hasher);
let hash = hasher.finish() as usize;
let index = hash % self.buckets.len();
&mut self.buckets[index]
}
I return a reference to the bucket because I don't want to move it out of the buckets
Vec of the hash table. And I return a mutable reference because I am going to mutate the returned bucket (for example, when inserting a new entry (key-value pair) into it).
The above code does compile.
But if I get rid of the intermediate variable index
and calculate the index right inside the []
brackets, like so:
fn pick_bucket(&mut self, key: K) -> &mut Bucket<K, V> {
let mut hasher = DefaultHasher::new();
key.hash(&mut hasher);
let hash = hasher.finish() as usize;
&mut self.buckets[hash % self.buckets.len()]
}
I will get this borrow error:
error[E0502]: cannot borrow `self.buckets` as immutable because it is also borrowed as mutable
--> src\lib.rs:30:34
|
26 | fn pick_bucket(&mut self, key: K) -> &mut Bucket<K, V> {
| - let's call the lifetime of this reference `'1`
...
30 | &mut self.buckets[hash % self.buckets.len()]
| -------------------------^^^^^^^^^^^^^^^^^^-
| | | |
| | | immutable borrow occurs here
| | mutable borrow occurs here
| returning this value requires that `self.buckets` is borrowed for `'1`
I thought the two code snippets above are equivalent. How do they differ, why does the first one compile, and why the second one does not?
Edit: I suppose the immutable borrow of self.buckets
in the first code snippet goes out of scope and gets "invalidated" right at the end of the line with let index = ...;
, so when the method returns, there's no shared references to self.buckets
left; and in the second snippet, the immutable borrow of self.buckets
lives right until the method returns (so at that moment both a shared and a mutable reference co-exist). If this is correct, why is this the case? Why doesn't the immutable borrow of self.buckets
get "invalidated" when reaching the ]
bracket in the second case, but instead live for the entire expression (the whole return line)?