I'm trying to implement Index
on my enum so that I can take slices of it (an enum of the same variant with a sliced inner value), following Implementing slice for custom type. But because my enum has variants with different types I can't get it to work:
pub enum GlkArray<'a> {
U8(&'a mut [u8]),
U32(&'a mut [u32]),
}
impl<'a, Idx> Index<Idx> for GlkArray<'a>
where Idx: SliceIndex<[usize]>
{
type Output = GlkArray<'a>;
fn index(&self, index: Idx) -> &Self::Output {
match self {
GlkArray::U8(buf) => &GlkArray::U8(&mut buf[index]),
GlkArray::U32(buf) => &GlkArray::U32(&mut buf[index]),
}
}
}
This results in these errors:
error[E0277]: the type `[u8]` cannot be indexed by `Idx`
--> remglk/src/glkapi/arrays.rs:117:53
|
117 | GlkArray::U8(buf) => &GlkArray::U8(&buf[index]),
| ^^^^^ slice indices are of type `usize` or ranges of `usize`
|
= note: required for `[u8]` to implement `Index<Idx>`
help: consider further restricting this bound
|
112 | where Idx: SliceIndex<[usize]> + std::slice::SliceIndex<[u8]>
| ++++++++++++++++++++++++++++++
error[E0277]: the type `[u32]` cannot be indexed by `Idx`
--> remglk/src/glkapi/arrays.rs:118:55
|
118 | GlkArray::U32(buf) => &GlkArray::U32(&buf[index]),
| ^^^^^ slice indices are of type `usize` or ranges of `usize`
|
= note: required for `[u32]` to implement `Index<Idx>`
help: consider further restricting this bound
|
112 | where Idx: SliceIndex<[usize]> + std::slice::SliceIndex<[u32]>
| +++++++++++++++++++++++++++++++
I kind of understand that SliceIndex
wants to be defined over a concrete type, but I'm not sure what to do in this situation. Yes you don't know what variant the enum has from outside, but it's returning another encapsulated enum, so I'm hoping that's okay.
Any ideas how Idx
needs to be defined, or any alternative suggestions of what I could do instead?
Unfortunately, you can't implement Index
for this type since it would require producing new &mut
references from behind a &
reference, which is impossible.
Even when Index
can't be implemented correctly, it's still possible that IndexMut
could be implemented by making a panicking Index
implementation, but IndexMut
is also impossible because of lifetime and ownership issues. When you mutably reborrow a slice, it must produce a slice with a new lifetime:
let mut x = [1, 2, 3];
// We'll say this reference has lifetime 'a
let slice = &mut x[..];
// This one has a new lifetime that's distinct from 'a
let slice2 = &mut slice[1..];
This means you can't return GlkArray<'a>
from IndexMut::index_mut
. And the trait is written so that it is impossible to specify a working lifetime for GlkArray
.
In addition, you can't return references to temporary values. Which means returning &mut GlkArray
will not work, since you must create a new GlkArray
inside the function. This also applies to Index
.
There is one way around both of these, which is to modify the original GlkArray
and return a reference to that. But this probably wouldn't be acceptable as a side effect of indexing.
Instead, you can write your own indexing function. You won't be able to use the indexing operator [_]
but it will otherwise work.
impl GlkArray<'_> {
// The return of this function cannot be Self
pub fn get_mut<'a, I>(&'a mut self, index: I) -> Option<GlkArray<'a>>
where
I: SliceIndex<[u8], Output = [u8]> + SliceIndex<[u32], Output = [u32]>,
{
match self {
Self::U8(slice) => slice.get_mut(index).map(GlkArray::U8),
Self::U32(slice) => slice.get_mut(index).map(GlkArray::U32),
}
}
}