如何创建和初始化不可变数组? [英] How do I create and initialize an immutable array?

查看:37
本文介绍了如何创建和初始化不可变数组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个数组.我不需要数组是可变的,并且在创建时,我拥有计算数组第 i 个成员所需的所有信息.但是,不知道如何在 Rust 中创建不可变数组.

I want to create an array. I don't need the array to be mutable, and at the time of creation, I have all the information I need to calculate the i-th member of the array. However, can't figure out how to create an immutable array in Rust.

这是我现在所拥有的:

let mut my_array: [f32; 4] = [0.0; 4];
for i in 0..4 {
    // some calculation, doesn't matter what exactly
    my_array[i] = some_function(i);
}

这就是我想要的:

let my_array: [f32; 4] = array_factory!(4, some_function);

如何在 Rust 中实现这一点?

How can I achieve that in Rust?

推荐答案

以下是宏定义和示例用法:

Here's the macro definition with sample usage:

macro_rules! array_factory(
    ($size: expr, $factory: expr) => ({
        unsafe fn get_item_ptr<T>(slice: *mut [T], index: usize) -> *mut T {
            (slice as *mut T).offset(index as isize)
        }

        let mut arr = ::std::mem::MaybeUninit::<[_; $size]>::uninit();
        unsafe {
            for i in 0..$size {
                ::std::ptr::write(get_item_ptr(arr.as_mut_ptr(), i), $factory(i));
            }
            arr.assume_init()
        }
    });
);

fn some_function(i: usize) -> f32 {
    i as f32 * 3.125
}

fn main() {
    let my_array: [f32; 4] = array_factory!(4, some_function);
    println!("{} {} {} {}", my_array[0], my_array[1], my_array[2], my_array[3]);
}

宏的主体本质上是您的第一个版本,但有一些变化:

The macro's body is essentially your first version, but with a few changes:

  • 数组变量上的类型注释被省略,因为它是可以推断的.
  • 数组是在未初始化的情况下创建的,因为无论如何我们都会立即覆盖所有值.使用未初始化的内存是不安全的,因此我们必须在 unsafe 块中对其进行操作.在这里,我们使用 MaybeUninit,它是在 Rust 1.36 中引入的,用于替换 <代码>内存::未初始化1.
  • 使用 std::ptr 分配项目::write() 因为数组未初始化.赋值会尝试删除数组中未初始化的值;效果取决于数组项类型(对于实现 Copy 的类型,如 f32,它没有效果;对于其他类型,它可能会崩溃).
  • 宏体是一个块表达式(即它用大括号括起来),该块以一个后面没有分号的表达式结束,arr.assume_init().因此,该块表达式的结果是 arr.assume_init().
  • The type annotation on the array variable is omitted, because it can be inferred.
  • The array is created uninitialized, because we're going to overwrite all values immediately anyway. Messing with uninitialized memory is unsafe, so we must operate on it from within an unsafe block. Here, we're using MaybeUninit, which was introduced in Rust 1.36 to replace mem::uninitialized1.
  • Items are assigned using std::ptr::write() due to the fact that the array is uninitialized. Assignment would try to drop an uninitialized value in the array; the effects depend on the array item type (for types that implement Copy, like f32, it has no effect; for other types, it could crash).
  • The macro body is a block expression (i.e. it's wrapped in braces), and that block ends with an expression that is not followed by a semicolon, arr.assume_init(). The result of that block expression is therefore arr.assume_init().

我们可以制作这个宏的安全版本,而不是使用不安全的功能;但是,它要求数组项类型实现 默认 特性.注意这里我们必须使用正常赋值,以确保正确删除数组中的默认值.

Instead of using unsafe features, we can make a safe version of this macro; however, it requires that the array item type implements the Default trait. Note that we must use normal assignment here, to ensure that the default values in the array are properly dropped.

macro_rules! array_factory(
    ($size: expr, $factory: expr) => ({
        let mut arr = [::std::default::Default::default(), ..$size];
        for i in 0..$size {
            arr[i] = $factory(i);
        }
        arr
    });
)

<小时>

1 这是有充分理由的.此答案的先前版本使用 mem::uninitialized,不是内存安全的:如果在初始化数组时发生恐慌(因为工厂函数恐慌),并且数组的项类型有析构函数,编译器会插入代码来调用数组中每一项的析构函数;甚至还没有初始化的项目!MaybeUninit 避免了这个问题,因为它将被初始化的值包装在 ManuallyDrop,这是 Rust 中防止析构函数自动运行的魔法类型.


1 And for a good reason. The previous version of this answer, which used mem::uninitialized, was not memory-safe: if a panic occurred while initializing the array (because the factory function panicked), and the array's item type had a destructor, the compiler would insert code to call the destructor on every item in the array; even the items that were not initialized yet! MaybeUninit avoids this problem because it wraps the value being initialized in ManuallyDrop, which is a magic type in Rust that prevents the destructor from running automatically.

这篇关于如何创建和初始化不可变数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆