How to implement Index<Range<usize>> for custom Vec and slice types?

I have types that compactly represent a sequence of elements by compressing multiple elements into a single number. Since all my elements are of the same size, I can index into this sequence, and I can take subsequences. Therefore I would like to implement Index<Range<usize>> to allow indexing with the [a..b] operator. Below is a simplified example.

use std::ops::{Index, Range};

struct VecWrapper {
    vec: Vec<i32>,
}

struct SliceWrapper {
    slice: [i32],
}

impl Index<Range<usize>> for VecWrapper {
    type Output = SliceWrapper;

    fn index(&self, index: Range<usize>) -> &Self::Output {
        &SliceWrapper {slice: self.vec[index]}
    }
}

(Playground)

This sadly does not compile, but throws the following error:

   Compiling playground v0.0.1 (/playground)
error[E0277]: the size for values of type `[i32]` cannot be known at compilation time
  --> src/lib.rs:15:10
   |
15 |         &SliceWrapper {slice: self.vec[index]}
   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: within `SliceWrapper`, the trait `Sized` is not implemented for `[i32]`
   = note: required because it appears within the type `SliceWrapper`
   = note: structs must have a statically known size to be initialized

Is it possible to make this code compile somehow, such that SliceWrapper is not Sized, but can still be returned in the index method?

Quoting the nomicon:

Currently the only properly supported way to create a custom DST is by making your type generic and performing an unsizing coercion [...] (Yes, custom DSTs are a largely half-baked feature for now.)

But you actually have a problem on top of that: index returns a &Self::Output, so you can't construct a Self::Output and return a reference to it. It has to be part of your struct somehow.

1 Like

@CAD97 -- is this a job for slice_dst?

1 Like

This doesn't need slice-dst, as SliceWrapper is just a wrapper around a slice without any other data. Instead, what you want is ref-cast, which allows you to cast from &[i32] to &SliceWrapper.

ref-cast currently only supports public casts (i.e. everyone can do the cast safely). If you need the cast to be private, you mark your wrapper type as #[repr(transparent)] and that allows you to reinterpret a reference to the wrapped type as a reference to your wrapper type with unsafe code. (See ref-cast for more details on the pattern; this is exactly what it does for you, just wrapped up in a safe derive-based API.)

2 Likes

Thanks both of you for your answers. I will try this out :slight_smile:

Thank you very much, that crate made it super easy. Now my type structures can be so much nicer than I thought!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.