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

Viewed 940

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?

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

I think this is a known issue (see also its related issue).

1 Answers

let x: MyStruct; doesn't set x to an empty value, it declares a variable. You still need to assign a value to it.

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

    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
}

In other words, let x; creates an unbound variable, a variable which doesn't have a value associated with it. Thus you need to bind some value to it later.

If you only want to return a value from the function, you can take advantage of the fact that almost every statement in Rust produces a value, and a value of the last statement is the return value of a function.

fn f(y: MyEnum) -> MyStruct {
    use MyEnum::*;

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

You can also completely eliminate x, if you so choose.

fn f(y: MyEnum) -> MyStruct {
    use MyEnum::*;

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