doctor 说:

Pub FN new(x:t)->box 在堆上分配内存,然后将x放入其中.

但"地方"是一个棘手的词.如果我们写下

let arr_boxed = Box::new([0;1000]);

[0;1000]是否会在堆上就地初始化?

如果我们写下

let arr = [0;1000];
let arr_boxed = Box::new(arr);

编译器是否足够聪明,能够在第一时间初始化堆上的[0;1000]

推荐答案

Box::new()会从一个堆栈复制到另一个堆吗?

有时候.Rust语言不能保证实现这种优化,似乎要由LLVM来解决这个问题. 正因为如此,即使初始化数组也无关紧要 首先,然后传递它,因为对于后端来说,这基本上是相同的事情.

在实践中,业绩将取决于具体情况. 您举的这个例子其实很特别,因为数据都是零:

pub fn foo() -> Box<[i32; 1000]> {
    return Box::new([0; 1000]);
}

在我的testing中,编译器能够将其转换为堆数据上的分配+对memset的调用.

Note:不过,只有在启用了优化的情况下.在调试模式下,它将复制.


另一方面,您可能希望使用已知值来初始化数据:

pub fn bar(v: i32) -> Box<[i32; 1000]> {
    return Box::new([v; 1000]);
}

Much to my horror,则编译器决定 初始化堆栈上的整个数据,然后调用memcpy.(至少它展开了填充循环):). 即使是像[v; 100000]这样的非常大的数据也会发生这种情况,这会使程序崩溃并导致堆栈溢出.使用编译时已知(非零)的文字,如[64; 100000],其行为与此相同.


如果你really想要确定,你可以这样做:

pub fn baz(v: i32) -> Box<[i32; 1000]>{
    unsafe {
        let b = std::alloc::alloc(
            std::alloc::Layout::array::<i32>(1000).unwrap_unchecked()
        ) as *mut i32;
        for i in 0..1000 {
            *b.add(i) = v;
        }
        Box::from_raw(b as *mut [i32; 1000])
    }
}

也就是the right thing美元.


baz的安全版本是:

use std::convert::TryInto;

pub fn quux(v: i32) -> Box<[i32; 1000]> {
    let mut b = Vec::with_capacity(1000);
    b.extend(std::iter::repeat(v).take(1000));
    b.into_boxed_slice().try_into().unwrap()
}

编译器将其优化为quite nicely,实质上是与baz相同的程序集.


更短的是

vec![v; 1000].into_boxed_slice().try_into::<Box<[i32; 1000]>>().unwrap()

这可能是最好的版本.

Rust相关问答推荐

如何在 struct 中填充缓冲区并同时显示它?

修改切片/引用数组

为潜在的下游实现使用泛型绑定而不是没有泛型绑定的trait

如何实现泛型枚举的`Serde::Desialize`特性

使用Box优化可选的已知长度数组的内存分配

通过RabbitMQ取消铁 rust 中长时间运行的人造丝任务的策略

写入引用会更新基础值,但引用会打印意外的值

为什么切片时需要参考?

在 Rust 中查找向量中 dyn struct 的索引

仅当函数写为闭包时才会出现生命周期错误

使用 traits 时,borrow 的值不会存在足够长的时间

在构建器模式中捕获 &str 时如何使用生命周期?

在异步 Rust 中,Future 如何确保它只调用最近的 Waker?

n 个范围的笛卡尔积

第 7.4 章片段中如何定义 `thread_rng`

如何在 Rust Polars 中可靠地连接 LazyFrames

Rustlings 切片原语

Cargo:如何将整个目录或文件包含在功能标志中?

如何断言代码不会在测试中编译?

传递 Option<&mut T> 时何时需要 mut