Why does the compiler warn about an uninitialized variable even though I've assigned each field of that variable?


#1

I’m completely assigning the fields of the MyStruct instance named x in every possible brace of the match:

enum MyEnum {
    One,
    Two,
    Three,
}

struct MyStruct {
    a: u32,
    b: u32,
}


fn main() {
    f(MyEnum::One);
    f(MyEnum::Two);
    f(MyEnum::Three);
}

fn f(y: MyEnum) -> MyStruct {
    let mut x: MyStruct;

    match y {
        MyEnum::One => {
            x.a = 1;
            x.b = 1;
        }
        MyEnum::Two => {
            x.a = 2;
            x.b = 2;
        }
        MyEnum::Three => {
            x.a = 3;
            x.b = 3;
        }
    }

    x
}

Why does the compiler return the following error? I mean: if all possible code path (every match brace) assigns the structure fields of the return value struct, why rustc complains?

error[E0381]: use of possibly uninitialized variable: `x`
  --> src/main.rs:37:5
   |
37 |     x
   |     ^ use of possibly uninitialized `x`

Thanks in advance.


#2

I guess Rust isn’t smart enough to see that the entire struct is initialized. Writing f like this works:

fn f(y: MyEnum) -> MyStruct {
    let x: MyStruct;

    match y {
        MyEnum::One => {
            x = MyStruct { a: 1, b: 1 };
        }
        MyEnum::Two => {
            x = MyStruct { a: 2, b: 2 };
        }
        MyEnum::Three => {
            x = MyStruct { a: 3, b: 3 };
        }
    }

    x
}

Playground link


#3

Simpler:

fn f(y: MyEnum) -> MyStruct {
    match y {
        MyEnum::One   => MyStruct { a: 1, b: 1 },
        MyEnum::Two   => MyStruct { a: 2, b: 2 },
        MyEnum::Three => MyStruct { a: 3, b: 3 },
    }
}

#4

This is issue #31947, which was marked as a duplicate of related issue #21232. According to the discussion there, this code may become valid in the future (i.e., it will be possible to read from a struct after each of its fields has been initialized separately).


#5

I will try to implement this form, thanks.


#6

I didn’t even realize that you can assign to struct field individually before initializing the whole struct. It feels like a noob trap.


#7

Fortunately forced me to a better implementation – but it’s kinda weird, sure.


#8

I didn’t even realize that you can assign to struct field individually before initializing the whole struct. It feels like a noob trap.

It was probably a C-ism that’s crept into the language. Initializing each member individually feels like a great way to accidentally leave something uninitialized, leading to the fun memory issues that Rust is designed to avoid.

I’d actually vote for assigning to fields individually before initialization to be a compile error (or at least a default-on warning) because it’s a great way to shoot yourself in the foot. It’s usually an anti-pattern in Rust anyway.


#9

How is this in any way dangerous? It’s just the reverse of partially deinitializing a struct by moving members out of it.

The rust compiler should not allow you to do anything with the object that will violate memory safety. If it does, that’s a bug.