【问题标题】:What is the proper way to initialize a fixed length array?初始化固定长度数组的正确方法是什么?
【发布时间】:2020-04-17 10:45:09
【问题描述】:

我在初始化固定长度数组时遇到问题。 My attempts so far all result in the same "use of possibly uninitialized variable: foo_array" error:

#[derive(Debug)]
struct Foo { a: u32, b: u32 }

impl Default for Foo {
    fn default() -> Foo { Foo{a:1, b:2} }
}

pub fn main() {
    let mut foo_array: [Foo; 10];

    // Do something here to in-place initialize foo_array?

    for f in foo_array.iter() {
        println!("{:?}", f);
    }
}
error[E0381]: use of possibly uninitialized variable: `foo_array`
  --> src/main.rs:13:14
   |
13 |     for f in foo_array.iter() {
   |              ^^^^^^^^^ use of possibly uninitialized `foo_array`

我实现了Default trait,但默认情况下,Rust 似乎不像 C++ 构造函数那样调用它。

初始化固定长度数组的正确方法是什么?我想做一个有效的就地初始化而不是某种复制。

相关:Why is the Copy trait needed for default (struct valued) array initialization?

相关:Is there a way to not have to initialize arrays twice?

【问题讨论】:

  • Rust 似乎没有默认调用它——这是正确的。编译器不会以任何特殊方式使用 Default 特征。它仅供程序员使用。

标签: arrays rust


【解决方案1】:

保险箱不过somewhat inefficient solution:

#[derive(Copy, Clone, Debug)]
struct Foo {
    a: u32,
    b: u32,
}

fn main() {
    let mut foo_array = [Foo { a: 10, b: 10 }; 10];
}

因为你是专门要求a solution without copies

use std::mem::MaybeUninit;

#[derive(Debug)]
struct Foo {
    a: u32,
    b: u32,
}

// We're just implementing Drop to prove there are no unnecessary copies.
impl Drop for Foo {
    fn drop(&mut self) {
        println!("Destructor running for a Foo");
    }
}

pub fn main() {
    let array = {
        // Create an array of uninitialized values.
        let mut array: [MaybeUninit<Foo>; 10] = unsafe { MaybeUninit::uninit().assume_init() };

        for (i, element) in array.iter_mut().enumerate() {
            let foo = Foo { a: i as u32, b: 0 };
            *element = MaybeUninit::new(foo);
        }

        unsafe { std::mem::transmute::<_, [Foo; 10]>(array) }
    };

    for element in array.iter() {
        println!("{:?}", element);
    }
}

这是the documentation of MaybeUninit推荐的。

【讨论】:

  • @A.B.:为什么第一个解决方案效率低下? (天真的问题,我真的不知道......)
  • 如果您需要构建一个元素彼此不同的数组,例如一组扑克牌,则效率低下。在标准的 52 张卡片组中,您最终会做 51 张不必要的副本。
  • 如果您在调用mem::uninitialized() 和数组完全初始化的那一点之间有任何恐慌的机会,那么这段代码就会被破坏并且不安全。不过,如果 Foo 是“POD”类型,那就没问题了。请注意,一旦您引入泛型(并在初始化循环中调用 trait 方法),您可能就无法再保证不出现恐慌了。
  • 在安全版本中:由于从不读取初始化值,编译器是否优化了副本? (或者可以吗?)
  • 如果FooString,则版本1 将不起作用,如果Foo 有1000 个成员,则不切实际。版本 2 还不如用 C 编写。必须有更好的方法,对吧?
【解决方案2】:

您可以使用arrayvec crate:

Cargo.toml

[package]
name = "initialize_array"
version = "0.1.0"
authors = ["author"]
edition = "2018"

[dependencies]
arrayvec = "0.4.10"

src/main.rs

use arrayvec::ArrayVec; 
use std::iter;

#[derive(Clone)]
struct Foo {
    a: u32,
    b: u32,
}

fn main() {
    let foo_array: [Foo; 10] = iter::repeat(Foo { a: 10, b: 10 })
        .collect::<ArrayVec<_>>()
        .into_inner()
        .unwrap_or_else(|_| unreachable!());
}

【讨论】:

  • 我认为您的代码在collect 之前缺少.take(10)
【解决方案3】:

最简单的方法是在你的类型上派生 Copy 并用它初始化数组,复制元素 N 次:

#[derive(Copy)]
struct Foo {
    a: u32,
    b: u32,
}

let mut foo_array = [Foo { a: 1, b: 2 }; 10];

如果您想避免复制,有几种选择。您可以使用 Default 特征:

let mut foo_array: [Foo; 10] = Default::default();

但是,这仅限于最多 32 个元素的数组。使用 const 泛型,标准库现在可以为 all 数组提供Default。但是,由于正在研究的微妙原因,这将是一个向后不兼容的更改。

目前,您可以利用 const 值也可以在数组重复表达式中使用这一事实:

const FOO: Foo = Foo { a: 1, b: 2 };

let mut foo_array = [FOO; 10];

如果你在夜间,你可以使用array::map

#![feature(array_map)]

let mut foo_array = [(); 10].map(|_| Foo::default())

【讨论】:

  • 您能否详细说明或指出与 Default 的向后兼容性问题的票证。它们对我来说并不明显。
  • @TheCycoONE 现在有一个impl&lt;T&gt; Default for [T; 0]。请注意,它不需要T: Default,因为它实际上并没有创建T。使用 const 泛型,目前没有办法专门化该 impl,所以毯子 impl&lt;T: Default, const N: usize&gt; Default for [T; N] 会被打破。
  • array::map 登陆稳定版!现在这是该问题的最佳答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-03
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
相关资源
最近更新 更多