能否从 Vec<T> 创建一个 Arc<[T]>?

15
更具体地说,为什么 Arc<T> 没有使用动态大小的 T 实现 from_raw,而 Box<T> 却实现了呢?请参考此处此处
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
}

(播放)

正如评论所指出的,Arc::from_raw 必须使用 Arc::into_raw 中的指针,因此上面的示例没有意义。我的原始问题 (是否可能从 Vec<T> 创建一个 Arc<[T]>) 仍然存在:这是可能的吗?如果不是,为什么?


1
也许这是一件好事:如果那段代码编译通过,那将是未定义行为,因为 Arc::from_raw 期望一个由 Arc::into_raw 返回的指针。尽管如此,from_raw 要求 T 具有大小可能会有一个很好的答案。 - E net4
@E_net4 真的吗?那么,在示例中写let x = Box::new(5);会导致未定义行为(UB)吗? - John
2
请注意,Arc::from_raw() 只能与从 Arc::into_raw() 返回的值一起使用,因为 Arc 在数据指针之前放置了一个头部,并且 Arc::from_raw 期望通过在您提供的指针之前立即查找该头部来找到它。 - Lily Ballard
1
请参阅 如何构建 Rc<str> 或 Rc<[T]>? - Shepmaster
3个回答

20
截至 Rust 1.21.0 版本,您可以这样做:
let thing: Arc<[i32]> = vec![1, 2, 3].into();

这是通过RFC 1845实现的:

此外: 将添加 From<Vec<T>> for Rc<[T]>From<Box<T: ?Sized>> for Rc<T>

相同的 API 也将为 Arc 添加。

在内部,它使用称为copy_from_slice的方法, 因此不会重新使用分配的 Vec。关于细节,请查看 DK.'s answer

6
首先,正如评论中已经指出的那样,您不能像这样随意地传递裸指针。引用自Arc::from_raw的文档

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

每次使用unsafe方法时,您绝对必须阅读文档。
其次,您想要的转换是不可能的。Vec → Box<[T]>之所以可行,是因为在内部,Vec实际上是(Box<[T]>, usize)对。因此,该方法只是让您访问该内部Box<[T]>指针[1]。然而,Arc<[T]>与Box不兼容,因为它必须包含引用计数。由Arc指向的东西与由Box指向的东西具有不同的大小和布局。
从Vec到Arc<[T]>的唯一方法是在一个引用计数分配中重新分配向量的内容...我不知道任何实现这种方法的方式。我不认为它不能被实现,只是还没有[2]。
尽管只能通过将指针强制转换为固定大小的类型来获得动态大小类型的Arc,但我认为无法在Arc::into_raw/Arc::from_raw中使用动态大小类型是一个错误。
[1]:不完全正确。Vec实际上没有Box<[T]>,但它有一些兼容的东西。它还必须缩小切片以不包含未初始化的元素。
[2]: 总的来说,Rust 并不支持在一般情况下动态分配大小的东西。也许这个缺陷的部分原因是 Box<T> 也不能直接分配数组,这可能是因为 Vec<T> 存在,因为 Vec<T> 曾经是语言本身的一部分,当已经存在 Vec 时,为什么要将数组分配添加到 Box 中呢?“那为什么不有一个 ArcVec<T> 呢?” 因为由于共享所有权,你永远无法 构造 一个。

3

Arc<[T]>是一个包含指向T切片的指针的Arc。但是[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)
}

播放链接

然而,你可以创建一个 Arc<&[T]> 而不必创建一个 boxed slice:

use std::sync::Arc;

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

共享 Ref 播放链接

然而,这似乎是一种类型系统的学习,实际价值很小。如果你真正想要的是一个可以在线程之间传递的 Vec 视图,那么 Arc<&[T]> 就可以满足你的需求。如果你需要它在堆上,Arc<Box<&[T]>> 也可以正常工作。


无法将 Arc<&[T]>Arc<Box<&[T]>> 传递给另一个(非作用域)线程,因为它们不能存活于 'static 生命周期。你是不是想要分别使用 Arc<[T]>Arc<Box<[T]>>?在这种情况下,Arc<[T]> 中的值已经与引用计数器一起位于堆上,因此通过 Box 进行第二次分配和间接引用是多余的。 - Mihail Malostanidis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接