更高效地初始化包含结构体的数组

3

I have the following code:

const N: usize = 10000;
const S: usize = 7000;

#[derive(Copy, Clone, Debug)]
struct T {
    a: f64,
    b: f64,
    f: f64
}

fn main() {
    let mut t: [T; N] = [T {a: 0.0, b: 0.0, f: 0.0}; N];

    for i in 0..N {
        t[i].a = 0.0;
        t[i].b = 1.0;
        t[i].f = i as f64 * 0.25;
    }

    for _ in 0..S {
        for i in 0..N {
            t[i].a += t[i].b * t[i].f;
            t[i].b -= t[i].a * t[i].f;
        }
        println!("{}", t[1].a);
    }
}

我不确定为什么数组t必须以那种方式初始化。第一个for循环旨在使用包含结构体的值来初始化数组中对应的值。

当我试图省略直接对数组进行初始化时:

let mut t: [T; N];

我得到以下错误:
错误[E0381]: 可能使用未初始化的变量:t
所有的for循环都是按照预期的方式编写的,我只想知道是否有一种更加聪明的方法来处理数组及其与第一个for循环的初始化。
2个回答

7
我不确定为什么数组必须以这种方式初始化。
因为Rust不允许您触摸(完全或部分)未初始化的值。编译器无法证明循环将绝对地初始化所有内容,因此它禁止了这样做。
现在,优化器是另一回事。理论上,它可以注意到初始化是多余的并跳过它...但在那段代码和当前编译器中似乎没有这样做。这就是优化。
我只想知道是否有更聪明的方法来处理数组及其与第一个for循环的初始化。
智慧的方法是保持代码不变。从统计学上讲,它不太可能成为瓶颈。如果分析表明它是一个瓶颈,那么你可以使用uninitialized。然而,请注意,如果使用不当,这样做可能会导致未定义的行为。虽然不是详尽无遗的列表,但你一定要避免在任何非Copy类型上使用它。
如果你确实需要使用它,我强烈建议同时调整第一个循环,使得忘记初始化元素或结构体中的字段变得不可能:
    let mut t: [T; N] = unsafe { ::std::mem::uninitialized() };

    for (i, e) in t.iter_mut().enumerate() {
        *e = T {
            a: 0.0,
            b: 1.0,
            f: i as f64 * 0.25,
        }
    }

如果您使用std::ptr :: write(e,t)而不是* e = t,是否可以消除仅限于“Copy”的限制? - ildjarn
4
@ildjarn:那与此无关。我之所以这么说是因为非Copy类型可以有自定义的Drop逻辑,如果你犯了错误并丢弃了未初始化的内存,就可能导致糟糕的结果。那些不需要遵循这个建议的人,通常都已经阅读了Rustonomicon,并且不太可能需要像我这样的建议。 - DK.

2

您可以使用std::mem::uninitialized()。然而,请注意,它被视为不安全的,并且需要标记为不安全:

let mut t: [T; N] = unsafe { std::mem::uninitialized() };

如前面链接的文档所述:

这对于FFI函数和初始化数组有时很有用,但通常应该避免使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接