I am writing an API for sparse arrays for my project. It looks like this:
// An non-contiguous array. E.g., an array with indices [0-99] and [200-299] valid, but not [100-199].
trait SparseArray<'a> {
type Item: 'a;
/* Using Borrow here so that I can return either an owned type or a borrowed type.
* For example, if I implement this using a HashMap, I will need to return a vector
* of references into the HashMap which are not contiguous. But if I implement this
* using a vector, I can just return a slice of the vector.
*/
type Slice: Borrow<[&'a Self::Item]>;
// get a contiguous block of the array, if the range is contiguous within the array
fn get_block(&'a self, range: Range<usize>) -> Option<Self::Slice>;
// set a contiguous block of the array, if start..start+values.len() is contiguous within the array
fn set_block<I, E>(&mut self, start: usize, values: I) -> Result<(), ()>
where
I: IntoIterator<IntoIter = E>,
E: ExactSizeIterator<Item = Self::Item>;
// check if the given range of indices is contiguous within the array
fn contains(&'a self, range: Range<usize>) -> bool;
}
I also wrote a module-level function which writes to a sparse array:
fn write_array<'a, A>(from: &[u8], to: &'a mut A, start: usize) -> Result<(), ()>
where
A: SparseArray<'a, Item = u8>
{
if !to.contains(start..start+from.len()) {return Err(());}
to.set_block(start, Vec::from(from))
}
When I tried to compile this, I got the following error:
error[E0502]: cannot borrow `*to` as mutable because it is also borrowed as immutable
--> src/lib.rs:35:5
|
29 | fn write_array<'a, A>(from: &[u8], to: &'a mut A, start: usize) -> Result<(), ()>
| -- lifetime `'a` defined here
...
34 | if !to.contains(start..start+from.len()) {return Err(());}
| ------------------------------------
| |
| immutable borrow occurs here
| argument requires that `*to` is borrowed for `'a`
35 | to.set_block(start, Vec::from(from))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground` (lib) due to previous error
I stared at this for a little bit and noted the message argument requires that *to is borrowed for 'a
. Which argument? Maybe the &'a self
argument to contains()
:
fn contains(&'a self, range: Range<usize>) -> bool;
Sure enough, when I removed the lifetime from this signature, the error disappeared:
fn contains(&self, range: Range<usize>) -> bool;
First, what is happening here? In the documentation of E0502, the immutable borrow let y = &a
is still in scope for the call to bar(a)
clearly because of its use in the following println!("{}", y)
. However in my case, I don't see why the immutable borrow of to
needs to remain in scope for the call to to.set_block()
if we can just drop the immutable borrow and create a new mutable one. I even tried to be explicit about this:
fn write_array<'a, A>(from: &[u8], to: &'a mut A, start: usize) -> Result<(), ()>
where
A: SparseArray<'a, Item = u8>
{
{
if !to.contains(start..start+from.len()) {return Err(());}
}
to.set_block(start, Vec::from(from))
}
But got the same error. So the error seems to be related to the lifetime on &'a self
in contains()
, but I'm not sure how.
Second, if the error is related to the signature of contains()
, then I think the compiler should have pointed me to the definition of contains.