Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to not have to initialize arrays twice?

I need to initialize each element of an array to a non-constant expression. Can I do that without having to first initialize each element of the array to some meaningless expression? Here's an example of what I'd like to be able to do:

fn foo(xs: &[i32; 1000]) {
    let mut ys: [i32; 1000];

    for (x, y) in xs.iter().zip(ys.iter_mut()) {
        *y = *x / 3;
    }
    // ...
}

This code gives the compile-time error:

error[E0381]: borrow of possibly uninitialized variable: `ys`
 --> src/lib.rs:4:33
  |
4 |     for (x, y) in xs.iter().zip(ys.iter_mut()) {
  |                                 ^^ use of possibly uninitialized `ys`

To fix the problem, I need to change the first line of the function to initialize the elements of ys with some dummy values like so:

let mut ys: [i32; 1000] = [0; 1000];

Is there any way to omit that extra initialization? Wrapping everything in an unsafe block doesn't seem to make any difference.

like image 568
kmky Avatar asked Oct 03 '14 19:10

kmky


People also ask

Can an array be initialized twice?

No. The array is not being created twice. It is being created once and then it is being populated.

What are the two ways to initialize arrays?

There are two ways to specify initializers for arrays: With C89-style initializers, array elements must be initialized in subscript order. Using designated initializers, which allow you to specify the values of the subscript elements to be initialized, array elements can be initialized in any order.

How do you initialize an array with one value?

int num[5] = {1, 1, 1, 1, 1}; This will initialize the num array with value 1 at all index. The array will be initialized to 0 in case we provide empty initializer list or just specify 0 in the initializer list. Designated Initializer: This initializer is used when we want to initialize a range with the same value.

What happens if an array is used without initializing it?

Even if you do not initialize the array, the Java compiler will not give any error. Normally, when the array is not initialized, the compiler assigns default values to each element of the array according to the data type of the element.


1 Answers

In some cases, you can use std::mem::MaybeUninit:

use std::mem::MaybeUninit;

fn main() {
    let mut ys: MaybeUninit<[i32; 1000]> = MaybeUninit::uninit();
}

Removing the MaybeUninit wrapper via assume_init is unsafe because accessing uninitialized values is undefined behavior in Rust and the compiler can no longer guarantee that every value of ys will be initialized before it is read.

Your specific case is one of the examples in the MaybeUninit docs; read it for a discussion about the safety of this implementation:

use std::mem::{self, MaybeUninit};

fn foo(xs: &[i32; 1000]) {
    // I copied this code from Stack Overflow without
    // reading why it is or is not safe.
    let ys: [i32; 1000] = {
        let mut ys: [MaybeUninit<i32>; 1000] = unsafe { MaybeUninit::uninit().assume_init() };

        let mut xs = xs.into_iter();

        for y in &mut ys[..] {
            if let Some(x) = xs.next().copied() {
                *y = MaybeUninit::new(x / 3);
            }
        }

        unsafe { mem::transmute(ys) }
    };
    // ...
}

You cannot collect into an array, but if you had a Vec instead, you could do:

let ys: Vec<_> = xs.iter().map(|&x| x / 3).collect();

For your specific problem, you could also clone the incoming array and then mutate it:

let mut ys = xs.clone();
for y in ys.iter_mut() { *y = *y / 3 }
like image 148
Dylan Avatar answered Oct 14 '22 08:10

Dylan