如何为 `Vec<i8>` 和 `&[i8]` 制作一个可索引的包装器?

How can I make an indexable wrapper for both `Vec<i8>` and `&[i8]`?

考虑以下代码:

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

编译器指出:

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

我应该如何设置这里的生命周期?我希望 Wrap 为拥有的 Vec 和借来的切片工作。最好的解决方案是什么?

Cow<[T]> 可能是您想要的:

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]);
}

这提供了一个适用于自有向量和借用切片的包装器,无需重新发明轮子:)。

原来的设计是不可能的。 Index 期望 index 方法 return 对类型 Self::Output:

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

我在上面扩展了生命周期以强调 returned 值 必须与 self 本身 一样长。当引用的值包含在被调用者中时,这是可以实现的,但包装值不是这种情况。在您的一次尝试中:

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

这将创建对仅存在于本地的 Wrap 对象的引用(因此不会超过 'a)。这种情况需要不同的特征,例如 WrapIndex,遗憾的是,它没有相同的语法糖。如果没有泛型关联类型 (GAT),它也不能泛化太多。

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

如果您不介意将切片类型的所有方法公开到 Wrap,您也可以为该包装器实现 Deref,从而免费获得索引和切片。

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

    fn deref(&self) -> &Self::Target {
        self.0.deref()
    }
}