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

时间:2018-10-10 10:02:08

标签: rust slice traits lifetime

考虑以下代码:

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和借来的切片工作。最好的解决方案是什么?

2 个答案:

答案 0 :(得分:2)

该原始设计是不可能的。 Index期望index方法返回对类型Self::Output的引用:

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

我在上面扩展了生存期,以强调返回值必须生存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()
    }
}

答案 1 :(得分:0)

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

  

它可以封装并提供对借入数据的不变访问权限,并在需要更改或所有权时懒惰地克隆数据

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

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