0

Consider the following code:

use std::ops;

struct Wrap<T>(T);

impl<T> Wrap<T> {
    fn new(element: T) -> Self {
        Wrap(element)
    }
}

// implementation of other methods that makes the wrapper necessary ;-)

impl ops::Index<ops::Range<usize>> for Wrap<Vec<i8>> {
    type Output = Wrap<&[i8]>;

    fn index(&self, range: ops::Range<usize>) -> &Self::Output {
        &Wrap::<&[i8]>::new(&self.0[range])
    }
}

impl ops::Index<ops::Range<usize>> for Wrap<&[i8]> {
    type Output = Wrap<&[i8]>;

    fn index(&self, range: ops::Range<usize>) -> &Self::Output {
        &Wrap::<&[i8]>::new(&self.0[range])
    }
}

playground

The compiler states:

error[E0106]: missing lifetime specifier
  --> src/lib.rs:14:24
   |
14 |     type Output = Wrap<&[i8]>;
   |                        ^ expected lifetime parameter

error[E0106]: missing lifetime specifier
  --> src/lib.rs:21:45
   |
21 | impl ops::Index<ops::Range<usize>> for Wrap<&[i8]> {
   |                                             ^ expected lifetime parameter

error[E0106]: missing lifetime specifier
  --> src/lib.rs:22:24
   |
22 |     type Output = Wrap<&[i8]>;
   |                        ^ expected lifetime parameter

How should I set the lifetimes here? I want Wrap to work for owned Vecs as well as borrowed slices. What would be the best solution?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
phil
  • 1,377
  • 2
  • 9
  • 14
  • Vec already implement [`as_slice()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.as_slice) so I don't see the point. Do you need [`Cow>`](https://doc.rust-lang.org/std/borrow/enum.Cow.html) ? – Stargateur Oct 10 '18 at 10:10
  • Yeah, I left out the parts that make the Wrapper necessary. I tried to focus the code on the problem. I know that there are other ways to implement this, but I think the best design would be a wrapper and feel this should be possible. – phil Oct 10 '18 at 10:40
  • Possible duplicate of [Implementing Index trait to return a value that is not a reference](https://stackoverflow.com/questions/39113649/implementing-index-trait-to-return-a-value-that-is-not-a-reference) – trent Oct 10 '18 at 12:49

2 Answers2

2

That original design is not possible. Index expects the index method to return a reference to the value of type Self::Output:

fn index<'a>(&'a self, index: Idx) -> &'a Self::Output;

I expanded the lifetime above to emphasize that the returned value must live for as long as self itself. This is achievable when the referenced value is contained in the callee, but this is not the case for a wrapped value. In one of your attempts:

fn index<'a>(&'a self, range: ops::Range<usize>) -> &'a Self::Output {
    &Wrap::<&[i8]>::new(&self.0[range])
}

This creates a reference to a Wrap object that only lives locally (and so does not outlive 'a). This case would call for a different trait, such as WrapIndex, which would, regretfully, not feature the same syntactic sugar. It also cannot be generalized much without generic associated types (GATs).

pub trait WrapIndex<Idx> where
    Idx: ?Sized, {
    type Output: ?Sized;
    fn wrap_index(&self, index: Idx) -> Wrap<&Self::Output>;
}

If you don't mind exposing all methods from a slice type into Wrap, you can also implement Deref for that wrapper, thus obtaining indexing and slicing for free.

impl<T> Deref for Wrap<T>
where T: Deref
{
    type Target = <T as Deref>::Target;

    fn deref(&self) -> &Self::Target {
        self.0.deref()
    }
}
E_net4
  • 27,810
  • 13
  • 101
  • 139
0

Cow<[T]> is probably what you are after:

it can enclose and provide immutable access to borrowed data, and clone the data lazily when mutation or ownership is required

use std::borrow::Cow;

fn main() {
    let w1: Cow<[i8]> = vec![1i8, 2, 3].into();
    let w2: Cow<[i8]> = (&[1i8, 2, 3][..]).into();

    println!("{:?}", &w1[1..2]);
    println!("{:?}", &w2[1..2]);
}

This provides a wrapper applicable to both owned vectors and borrowed slices without the need to reinvent the wheel :).

ljedrz
  • 20,316
  • 4
  • 69
  • 97
  • `Cow` is a run-time checked wrapper. It does not seem to relate to the question at all. – E_net4 Oct 10 '18 at 10:48
  • @E_net4 there was no restriction on compile-time, no? That being said, I didn't think it incurs any runtime penalties; can you provide some reference? I'd like to know more. – ljedrz Oct 10 '18 at 10:50
  • Thanks for the answer. `Cow` is a good hint, but I really would like my own wrapper where I can implement own methods on. I left those out to focus the code on the problem. – phil Oct 10 '18 at 10:50
  • Each time cow is written the struct need to check if it had clone the data or not. – Stargateur Oct 10 '18 at 11:01
  • To expand on the subject: [`Cow`](https://doc.rust-lang.org/std/borrow/enum.Cow.html) is an enum. It contains either owned content or borrowed content, along with a discriminant value. This requires the program to check at run-time whether it is currently working with owned data or borrowed data. – E_net4 Oct 10 '18 at 12:59