rust 初始化具有非Copy类型的大型固定大小数组

u0njafvf  于 2022-12-29  发布在  其他
关注(0)|答案(8)|浏览(214)

我试图初始化一个固定大小的数组,它是一个不可复制的、可以为空的类型,比如Option<Box<Thing>>,我想把其中的两个数组打包成一个结构体,而不需要额外的间接寻址,我想写这样的代码:

let array: [Option<Box<Thing>>; SIZE] = [None; SIZE];

但它不起作用,因为[e; n]语法要求e实现Copy。当然,我可以将其扩展为SIZENone s,但当SIZE很大时,这可能会很笨拙。我不相信如果不对SIZE进行非自然编码,就可以使用宏来完成这一操作。有好的方法吗?
是的,使用unsafe很容易;有没有办法不用unsafe

prdp8dxp

prdp8dxp1#

另一种方法使用arrayvec crate,它可以很容易地推广到除使用固定值初始化所有内容之外的情况:

use arrayvec::ArrayVec;

let array = std::iter::repeat(None)
    .take(SIZE)
    .collect::<ArrayVec<Option<Box<Thing>>, SIZE>>()
    .into_inner()
    .unwrap();

playground

jljoyd4f

jljoyd4f2#

let stackoverflow: [Option<&mut ()>;0xDEADBEEF] = std::array::from_fn(|_| None);
dbg!(stackoverflow);

playground

ipakzgxi

ipakzgxi3#

在每夜运行的Rust中,你可以使用inline const,这是@user4815162342的一个变体,但它不需要你声明一个单独的常量并重复该类型:

#![feature(inline_const)]

let array: [Option<Box<Thing>>; SIZE] = [const { None }; SIZE];

在稳定之前(希望很快),您还可以使用inline-const crate,但这确实需要您重复输入。

xfb7svmp

xfb7svmp4#

从Rust 1.38(2019年9月发布)开始,使用中间的const初始化器可以得到一个比之前发布的答案更简洁的替代方案,这种方法适用于任何大小的数组:

const SIZE: usize = 100;
const INIT: Option<Box<Thing>> = None;
let array: [Option<Box<Thing>>; SIZE] = [INIT; SIZE];

(It可与Box配合使用或不配合使用;本示例使用Box,因为问题中使用了它。)
一个限制是数组项必须有一个可以在编译时求值的默认表示-常量、枚举变量或由这些组成的原语容器。None或数字元组可以工作,但非空的VecString不行。

gzjq41n4

gzjq41n45#

您可以使用Default trait将数组初始化为默认值:

let array: [Option<Box<Thing>>; SIZE] = Default::default();

有关工作示例,请参见this playground
请注意,这只适用于最多包含32个元素的数组,因为Default::default只适用于最多[T; 32]https://doc.rust-lang.org/std/default/trait.Default.html#impl-Default-for-%5BT%3B%2032%5D。

sirbozc5

sirbozc56#

从Rust1.55.0(引入了[T]::map())开始,以下代码将起作用:

const SIZE: usize = 100;

#[derive(Debug)]
struct THING { data: i64 }

let array = [(); SIZE].map(|_| Option::<THING>::default());
for x in array {
    println!("x: {:?}", x);
}

Rust Playground

u2nhd7ah

u2nhd7ah7#

我正在复制the answer by chris-morgan并对其进行修改,以便更好地匹配问题,遵循dbaupp downthread的建议,并匹配最近的语法更改:

use std::mem;
use std::ptr;

#[derive(Debug)]
struct Thing {
    number: usize,
}

macro_rules! make_array {
    ($n:expr, $constructor:expr) => {{
        let mut items: [_; $n] = mem::uninitialized();
        for (i, place) in items.iter_mut().enumerate() {
            ptr::write(place, $constructor(i));
        }
        items
    }}
}

const SIZE: usize = 50;

fn main() {
    let items = unsafe { make_array!(SIZE, |i| Box::new(Some(Thing { number: i }))) };
    println!("{:?}", &items[..]);
}

注意这里需要使用unsafe:问题是,如果构造函数panic! s,这将导致未定义的行为。

5hcedyr0

5hcedyr08#

穿过堆

如果可以创建类型为Vec的对象,则可以将其转换为数组:

use std::convert::TryInto;

#[derive(Clone)]
struct Thing;
const SIZE: usize = 100;

fn main() {
    let v: Vec<Option<Thing>> = vec![None; SIZE];
    let v: Box<[Option<Thing>; SIZE]> = match v.into_boxed_slice().try_into() {
        Ok(v) => v,
        Err(_) => unreachable!(),
    };
    let v: [Option<Thing>; SIZE] = *v;
}

在很多情况下,你实际上想把它保留为Vec<T>Box<[T]>Box<[T; N]>,因为这些类型都把数据放在堆中,大型数组往往很大,你不想把所有的数据都放在堆栈上。
另见:

保持简单

键入所有值:

struct Thing;
const SIZE: usize = 5;

fn main() {
    let array: [Option<Box<Thing>>; SIZE] = [None, None, None, None, None];
}

您可以使用a build script为您生成此代码。有关此代码的示例,请参见:

相关问题