【问题标题】:Is it possible to create an Arc<[T]> from a Vec<T>?是否可以从 Vec<T> 创建 Arc<[T]>?
【发布时间】:2017-10-13 01:57:32
【问题描述】:

更具体地说,为什么 doesn't Arc&lt;T&gt; 使用动态大小的 T 而实现 from_rawBox&lt;T&gt; does

use std::sync::Arc;

fn main() {
    let x = vec![1, 2, 3].into_boxed_slice();
    let y = Box::into_raw(x);
    let z = unsafe { Arc::from_raw(y) }; // ERROR
}

(play)

正如 cmets 中所指出的,Arc::from_raw 必须与来自 Arc::into_raw 的指针一起使用,因此上面的示例没有意义。我最初的问题(Is it possible to create an Arc&lt;[T]&gt; from a Vec&lt;T&gt;)仍然存在:这可能吗,如果不是,为什么?

【问题讨论】:

  • 也许这是件好事:如果编译该代码,它将是 UB,因为 Arc::from_raw 期望 Arc::into_raw 返回一个指针。不过,from_raw 需要 T 调整大小的部分可能会有一个很好的答案。
  • @E_net4 真的吗?那么,在示例中写let x = Box::new(5); 将是UB?
  • 请注意,Arc::from_raw() 只能与从Arc::into_raw() 返回的值一起使用,因为Arc 在数据指针之前放置了一个标头,而Arc::from_raw 期望通过紧接之前查找来找到该标头您提供的指针。

标签: rust


【解决方案1】:

从 Rust 1.21.0 开始,您可以这样做:

let thing: Arc<[i32]> = vec![1, 2, 3].into();

这是由RFC 1845启用的:

另外:From&lt;Vec&lt;T&gt;&gt; for Rc&lt;[T]&gt;From&lt;Box&lt;T: ?Sized&gt;&gt; for Rc&lt;T&gt; 将被添加。

还将为 Arc 添加相同的 API。

在内部,这个uses a method called copy_from_slice,所以Vec的分配没有被重用。详情请查看DK.'s answer

【讨论】:

    【解决方案2】:

    没有。

    首先,正如在 cmets 中已经指出的那样,您不能像那样随意地扔原始指针。引用the documentation of Arc::from_raw:

    原始指针必须先前已通过调用 Arc::into_raw 返回。

    在您使用unsafe 方法时,您绝对必须阅读文档。

    其次,你想要的转换是不可能的。 Vec&lt;T&gt;Box&lt;[T]&gt; 有效,因为在内部,Vec&lt;T&gt; 实际上是 (Box&lt;[T]&gt;, usize) 对。因此,该方法所做的只是让您访问该内部 Box&lt;[T]&gt; 指针 [1]。然而,Arc&lt;[T]&gt;Box&lt;[T]&gt; 在物理上兼容,因为它必须包含引用计数。 Arc&lt;T&gt; 指向的对象与Box&lt;T&gt; 指向的对象的大小和布局不同。

    Vec&lt;T&gt;Arc&lt;[T]&gt; 的唯一方法是在引用计数分配中重新分配向量的内容...我不知道有什么办法。我不认为有任何特殊原因无法实施,只是没有 [2]。

    话虽如此,我相信不能使用 Arc::into_raw/Arc::from_raw 的动态大小类型是一个错误。当然有可能获得具有动态大小类型的Arcs...尽管只能通过从指针转换为固定大小的类型。


    [1]:不完全是。 Vec&lt;T&gt;实际上里面没有Box&lt;[T]&gt;,但它有一些东西兼容。它还必须缩小切片以不包含未初始化的元素。

    [2]:总的来说,Rust 对分配动态大小的东西没有很好的支持。这个漏洞的部分原因可能是Box&lt;T&gt;不能直接分配数组,这可能是因为Vec&lt;T&gt;存在,因为Vec&lt;T&gt;曾经是语言本身,当Vec 已经存在时,为什么要向Box 添加数组分配? “那为什么不拥有ArcVec&lt;T&gt;?”因为由于共享所有权,你永远无法构建一个。

    【讨论】:

      【解决方案3】:

      Arc&lt;[T]&gt; 是一个Arc,包含一个指向T 切片的指针。但是[T] 在编译时实际上并没有调整大小,因为编译器不知道它将持续多长时间(与 &[T] 相比,它只是一个参考,因此具有已知的大小)。

      use std::sync::Arc;
      
      fn main() {
          let v: Vec<u32> = vec![1, 2, 3];
          let b: Box<[u32]> = v.into_boxed_slice();
          let y: Arc<[u32]> = Arc::new(*b);
          print!("{:?}", y)
      }
      

      Play Link

      但是,您可以制作 Arc&lt;&amp;[T]&gt; 而不制作盒装切片:

      use std::sync::Arc;
      
      fn main() {
          let v = vec![1, 2, 3];
          let y: Arc<&[u32]> = Arc::new(&v[..]);
          print!("{:?}", y)
      }
      

      Shared Ref Play Link

      但是,这似乎是对类型系统的研究,没有什么实用价值。如果您真正想要的是可以在线程之间传递的 Vec 视图,那么 Arc 将为您提供所需的内容。如果您需要它在堆上,Arc&lt;Box&lt;&amp;[T]&gt;&gt; 也可以正常工作。

      【讨论】:

        猜你喜欢
        • 2020-01-22
        • 1970-01-01
        • 1970-01-01
        • 2010-12-07
        • 1970-01-01
        • 2018-08-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多