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

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

问题描述

我想创建一个数组。我不需要数组是可变的,在创建时,我拥有计算数组第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.

这是我现在拥有的内容:

Here's what I have now:

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中引入,用于替换 mem :: uninitialized 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天全站免登陆