自从我来到这里,其他人也可能会来到这里。Rust已经发展了起来,在本答案发布时,Rust稳定版本为1.53,夜间版本为1.55。
使用
Box::new([1, 2, 3])
是推荐的方式,它能够完成任务,但有一个注意点:数组首先在栈上被创建,然后再复制到堆上。这是Box文档中所记录的行为:
通过创建Box将一个值从栈移动到堆上:
这意味着它包含了一个隐藏的
memcopy
,当处理大型数组时,堆分配甚至会因堆栈溢出而失败。
const X: usize = 10_000_000;
let failing_huge_heap_array = [1; X];
thread 'main' has overflowed its stack
fatal runtime error: stack overflow
目前(Rust 1.53),有几种解决方法,最简单的方法是创建一个向量并将该向量转换为一个盒式切片:
const X: usize = 10_000_000;
let huge_heap_array = vec![1; X].into_boxed_slice();
这个方法是可行的,但有两个小问题:它丢失了类型信息,本应是Box<[i32; 10000000]>现在变成了Box<[usize]>,此外在栈上占用了16个字节,而数组只占用8个字节。
...
println!("{}", mem::size_of_val(&huge_heap_array);
16
可能不是什么大问题,但这违背了我的个人修行。
进一步研究后,我排除了那些需要像OPbox [1, 2, 3]
那样使用#![feature(box_syntax)]
功能和arr crate(也需要夜间版本)的选项,我发现在堆上分配数组且没有隐藏的memcopy
的最佳解决方案是由Simias提出的建议。
macro_rules! box_array {
($val:expr ; $len:expr) => {{
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])
}};
}
const X: usize = 10_000_000;
let huge_heap_array = box_array![1; X];
它不会溢出堆栈,只占用8个字节,同时保留类型。
它使用了不安全的操作,但将其限制在单行代码中。在 box [1;X]
语法到来之前,我认为这是一种干净的选择。
box () [1, 2, 3]
重写为可能需要在[1, 2, 3]
周围加上额外括号的形式。但现在你不需要这样做。 - Chris Morgan