【问题标题】:Return a &Vec<T> from a VecDeque<T> field从 VecDeque<T> 字段返回 &Vec<T>
【发布时间】:2021-06-24 15:49:32
【问题描述】:

我有一个带有VecDeque&lt;T&gt; 字段和get() -&gt; Option&lt;&amp;Vec&lt;T&gt;&gt; 方法的类型:

struct Storage<T> {
    buffer: VecDeque<T>
}

impl<T> Storage<T> {
    fn get(&self, /* args */ ) -> Option<&Vec<T>> { /* ... */ }
}

根据get() 的输入参数,我想返回一个Option&lt;&amp;Vec&lt;T&gt;&gt;,它实质上是指buffer 内从索引i 到索引j 的切片:

index  =  0 ... i ... j  ... (n-1)
buffer = [a, b, c, d, e, f, g, h]
                |-----|
             return &Vec<T>

到目前为止,在我的尝试中,我发现VecDeque::as_slices() 可能会有所帮助(我确保在调用get() 之前缓冲区始终是连续的)并且我还看到有一些方法可以将切片转换为Vec&lt;T&gt;。它的参考部分&amp;Vec&lt;T&gt; 我似乎遇到了麻烦。

附加细节: get() 方法实际上来自于一个 trait 的实现:

trait Storable {
    type OutputType;
    fn get(&self, /* args */) -> Option<&Self::OutputType>;
}

此 API 遵循 slab 板条箱中的 Slab 类型,事实上,为 Slab 实现 Storable 非常简单。一种可能性是我将OutputType 关联类型从Vec&lt;T&gt; 更改为其他类型。但是,在我的代码的其他地方,我可能会严重依赖此关联类型是Vec&lt;T&gt; 的假设。这可能是代码的味道。无论解决方案是什么,出于性能原因,我都希望尽可能避免复制/克隆。

【问题讨论】:

  • 您确定需要&amp;Vec&lt;T&gt;吗? &amp;Vec&lt;T&gt; 几乎没有用处,也许你可以用 &amp;[T] 代替。为此,您可以设置type OutputType = [T],这将导致Storable::get() 返回Option&lt;&amp;[T]&gt;。如果您确保 VecDeque 是连续的,那应该正是您所需要的。
  • 我构建的实现Storable trait 的其他类型的输入/输出是Vec&lt;T&gt;。由于这些其他类型存储Vec&lt;T&gt;s 的方式,存储类型可以拥有这些vecs。但是,我在上面创建的使用 VecDeque&lt;T&gt; 的类型实际上并不存储向量,因此存在问题。虽然我不反对,但我需要看看切换到 &amp;[T] 类型的影响。
  • 听起来你已经把自己画到了一个抽象的角落里。我并不是说作为一种批评,它确实发生在每个从事非平凡编程的人身上。祝你找到一个可行的优雅解决方案!
  • 至少客厅的其他部分看起来不错!

标签: rust


【解决方案1】:

我相信这不可能以您想象的方式发生,因为所有权规则:

如果您将双端队列作为切片,那么您将获得单个连续切片,这本质上是一个不可变的借用。这意味着切片不拥有这些值。它的类型是&amp;[T]

另一方面,现在Vec&lt;T&gt; 想要拥有 T 类型的值。因此从&amp;[T] 创建Vec&lt;T&gt; 将不得不涉及副本或克隆,取决于T 是否支持这些操作。

您想要&amp;Vec&lt;T&gt; 的事实并没有改变您需要在堆上创建实际Vec&lt;T&gt; 的事实,而这又需要更改所有权。

返回&amp;Vec&lt;T&gt; 的另一个问题是生命周期。如果这个 Vec 是新创建的,通过你认为适合你的任何转换,它只会在它存在时才有效,这将是 get 函数:

impl<T> Storage<T> {
  fn get(&self, /* args*/ ) -> Option<&Vec<T>> {
    let return_vec: Vec<T> = /* some code */;
    /* some more code */
    Ok(&return_vec); // Uh oh!
  }
}

编译器会抱怨您无法返回对函数中本地内容的引用。

我认为最好的选择是将输出类型更改为切片&amp;[T]。您说在您自己的内部代码中,您严重依赖 vec 表示。现在的好处是,通过一些智能 trait 实现,任何期望 &amp;[T] 作为参数的函数也适用于 Vec&lt;T&gt;

【讨论】:

  • 您对 Vec&lt;T&gt; 想要拥有其价值观的评论使我的问题变得清晰。您可以将我的问题改写为:“Rust 是否有 Span C++ 对象”? (非拥有向量。)return_vec 示例实际上是我失败的尝试之一。就像你说的,&amp;[T] 应该不是什么大问题。我们拭目以待!
  • 非拥有向量的类型为Vec&lt;&amp;T&gt;
  • Vec&lt;&amp;T&gt; 的工作方式如下:play.rust-lang.org/…
  • 我在上面评论说Option&lt;&amp;OutputType&gt; 设计是从Slab 借来的,而我对Storable 的其他一些实现自然地遵循了这个设计。 (例如,基于HashMap 的存储将拥有它的Vec&lt;T&gt;
猜你喜欢
  • 1970-01-01
  • 2020-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-15
  • 1970-01-01
相关资源
最近更新 更多