如何在Rust 1.0中在堆上分配数组?

22

已经有一个关于这个问题的问题,但与Rust 0.13相关且语法似乎已更改。从当前的文档中,我了解到在堆上创建数组应该是这样的:

fn main() {
    const SIZE: usize = 1024 * 1024;
    Box::new([10.0; SIZE]);
}

但是当我运行这个程序时,我收到了以下错误:

thread '<main>' has overflowed its stack

我做错了什么?

3个回答

26
问题在于数组作为参数被传递到Box::new函数中,这意味着必须先创建它,这意味着它必须在堆栈上创建
你要求编译器在堆栈上创建8兆字节的数据:这就是导致溢出的原因。
解决方案是根本不使用固定大小的数组,而是使用Vec。我能想到的最简单的方法是创建一个由800万个10.0构成的Vec
fn main() {
    const SIZE: usize = 1024 * 1024;
    let v = vec![10.0; SIZE];
}

或者,如果由于某种原因您更愿意使用迭代器:

use std::iter::repeat;

fn main() {
    const SIZE: usize = 1024 * 1024;
    let v: Vec<_> = repeat(10.0).take(SIZE).collect();
}

这个操作应该只进行一次堆内存分配。

请注意,您可以随后使用into_boxed_slice方法将一个Vec转换为Box<[_]>

另请参阅:


2
请注意,目前有一个名为box的语法,它应该有助于解决这个问题,但它目前还不稳定,我不确定它是否支持直接将数组放置在堆上。 - Vladimir Matveev
5
你可以使用 vec![10.0; 1024 * 1024] - BurntSushi5

12

接受的答案非常不令人满意,因为有时我们确实希望在堆上分配数组以保留类型级别的大小信息。使用常量泛型可以稍微改进Johannes的答案。我们可以使用以下函数来代替宏:

fn vec_to_boxed_array<T: Copy, const N: usize>(val: T) -> Box<[T; N]> {
    let boxed_slice = vec![val; N].into_boxed_slice();

    let ptr = Box::into_raw(boxed_slice) as *mut [T; N];

    unsafe { Box::from_raw(ptr) }
}

10
我也遇到了这个问题,最终先参考了在Rust中在堆上创建固定大小的数组得到完整的答案。
这个回答的要点是这个宏:
/// A macro similar to `vec![$elem; $size]` which returns a boxed array.
///
/// ```rustc
///     let _: Box<[u8; 1024]> = box_array![0; 1024];
/// ```
macro_rules! box_array {
    ($val:expr ; $len:expr) => {{
        // Use a generic function so that the pointer cast remains type-safe
        fn vec_to_boxed_array<T>(vec: Vec<T>) -> Box<[T; $len]> {
            let boxed_slice = vec.into_boxed_slice();

            let ptr = ::std::boxed::Box::into_raw(boxed_slice) as *mut [T; $len];

            unsafe { Box::from_raw(ptr) }
        }

        vec_to_boxed_array(vec![$val; $len])
    }};
}

我最喜欢的是,它会给你OP想要的东西:
一个数组,并且在稳定的Rust环境下可以工作。

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