【问题标题】:How can I make an indexable wrapper for both `Vec<i8>` and `&[i8]`?如何为 `Vec<i8>` 和 `&[i8]` 创建一个可索引的包装器?
【发布时间】:2019-03-15 05:03:29
【问题描述】:

考虑以下代码:

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 为拥有的Vecs 以及借来的切片工作。最好的解决方案是什么?

【问题讨论】:

标签: rust slice traits lifetime


【解决方案1】:

那个原创设计是不可能的。 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()
    }
}

【讨论】:

    【解决方案2】:

    Cow&lt;[T]&gt; 可能就是你所追求的:

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

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

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

    【讨论】:

    • Cow 是一个运行时检查的包装器。它似乎根本与问题无关。
    • @E_net4 对编译时间没有限制,不是吗?话虽如此,我认为它不会产生任何运行时惩罚。你能提供一些参考吗?我想知道更多。
    • 感谢您的回答。 Cow 是一个很好的提示,但我真的很想要我自己的包装器,我可以在其中实现自己的方法。我忽略了这些,以便将代码集中在问题上。
    • 每次写cow的时候,结构体需要检查它是否克隆了数据。
    • 扩展主题:Cow 是一个枚举。它包含拥有的内容或借用的内容,以及判别值。这要求程序在运行时检查它当前是在使用自有数据还是借用数据。
    猜你喜欢
    • 2016-12-14
    • 1970-01-01
    • 1970-01-01
    • 2014-01-22
    • 1970-01-01
    • 2012-03-11
    • 2022-10-24
    • 2011-01-04
    • 1970-01-01
    相关资源
    最近更新 更多