如何动态设置Rust数组的长度?

79

我想创建这样的数组:

let arr = [0; length];

当长度为usize类型时。但是我收到了这个错误。

E0307
The length of an array is part of its type. For this reason, this length 
must be a compile-time constant.

是否可以创建动态长度的数组?我想要一个数组,而不是一个Vec


4
我不认为这是可能的。为什么你不想使用一个Vec呢? - fjh
8
也许你应该描述一下哪些 Vec 的特性是不如数组的。 - Brian Cain
或者可以使用Slice,它类似于Array,但是Slice的大小不需要在编译时知道。 - Akavall
我有一些接受数组引用的函数,因此我需要一个数组。 - Dima Kudosh
7
@DimaKudosh,您确定他们接受对数组的引用而不是切片(例如&[u32])吗?如果实际上是切片,那么Vec<T>可以自由转换为&[T],通常只需使用&let v: Vec<u32> = ...; let s: &[u32] = &v。如果他们确实接受对数组的引用,那么动态大小的数组如何帮助您?在这种情况下,Rust没有类型级整数,因此数组的大小必须是恒定的。 - Vladimir Matveev
显示剩余5条评论
3个回答

97

能否创建一个动态长度的数组?不行。根据定义,数组的长度在编译时被定义。变量(因为它们可以变化)在编译时是未知的。编译器不会知道要分配多少空间在堆栈上为数组提供存储。

您需要使用Vec

let arr = vec![0; length];

另见:


那么Vec::into_boxed_slice是一个选项吗?它与普通的Vec或数组相比如何? - iago-lito
2
@iago-lito,into_boxed_slice()方法有什么用途?一个Vec和一个boxed slice的性能比较 这两篇回答您的问题了吗? - Shepmaster

15

1
我真的希望他们能够实现这个。我想用Rust代替C++,只有在他们选择不犯C++所犯的错误时,我才会信任他们。C++没有使用指定初始化器,而是随意给出“理由”,可变长度数组(VLAs)非常相似。它们是性能计算的基石,因为它们有助于防止缓存未命中,这也是推测执行优化的重要辅助工具。C#开发人员通过引入“stackalloc”做了正确的事情,相信我,这对于下一代优化确实是必需的。 - Abdurrahim
这种动态数组与Vec相比有什么优势呢?它们位于栈上而不是堆上,还有其他方面的优点吗?Vec的布局已经在堆上尽可能高效了,这样说对吗?对于更大尺寸的数组,栈就不适用了,对吗? - undefined

2
你可以创建自己的HeapArray。如果你阅读alloc的文档,这并不是很复杂。
use std::alloc::{alloc, dealloc, Layout};
pub struct HeapArray<T> {
    ptr: *mut T,
    len: usize,
}

impl<T> HeapArray<T> {
    pub fn new(len: usize) -> Self {
        let ptr = unsafe {
            let layout = Layout::from_size_align_unchecked(len, std::mem::size_of::<T>());
            alloc(layout) as *mut T
        };
        Self { ptr, len }
    }
    pub fn get(&self, idx: usize) -> Option<&T> {
        if idx < self.len {
            unsafe { Some(&*(self.ptr.add(idx))) }
        } else {
            None
        }
    }
    pub fn get_mut(&self, idx: usize) -> Option<&mut T> {
        if idx < self.len {
            unsafe { Some(&mut *(self.ptr.add(idx))) }
        } else {
            None
        }
    }
    pub fn len(&self) -> usize {
        self.len
    }
}


impl<T> Drop for HeapArray<T> {
    fn drop(&mut self) {
        unsafe {
            dealloc(
                self.ptr as *mut u8,
                Layout::from_size_align_unchecked(self.len, std::mem::size_of::<T>()),
            )
        };
    }
}

impl<T> std::ops::Index<usize> for HeapArray<T> {
    type Output = T;
    fn index(&self, index: usize) -> &Self::Output {
        self.get(index).unwrap()
    }
}
impl<T> std::ops::IndexMut<usize> for HeapArray<T> {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        self.get_mut(index).unwrap()
    }
}


你还可以添加像as_sliceget_unchecked等方法。

from_size_align_uncheckedsize 参数是以字节为单位的。因此,您必须将 self.len 乘以 std::mem::size_of::<T>() 来计算总大小。 - Majid Azimi
2
这与 Vec 有何不同?它像 Vec 一样进行堆分配。 - Neil Mayhew
18
这段代码百分之百存在内存不安全问题,不应该被任何人使用。 let ha = HeapArray::<String>::new(10); println!("{}", ha[7]); 这段代码会导致段错误。正常情况下也会导致内存泄漏。编写有缺陷的代码并不是很复杂。 - Shepmaster

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