Use of possibly uninitialized variable on an initialized variable?


#1

Hi

I started messing around with rust this weekend and am confused why I’m getting the ‘use of possibly uninitialized variable’ error for the following example code.

use std::sync::mpsc::{channel,Receiver,Sender};

struct Worker {
  chan: (Sender<usize>, Receiver<usize>)
}

fn main() {
  let mut worker: Worker;
  worker.chan = channel();
  let (tx, rx) = worker.chan;
}

Attempting to compile this results in the following

$ rustc test.rs 
test.rs:10:8: 10:10 error: use of possibly uninitialized variable: `worker.chan.0` [E0381]
test.rs:10   let (tx, rx) = worker.chan;
                  ^~
test.rs:10:8: 10:10 help: run `rustc --explain E0381` to see a detailed explanation
test.rs:10:12: 10:14 error: use of possibly uninitialized variable: `worker.chan.1` [E0381]
test.rs:10   let (tx, rx) = worker.chan;
                      ^~
test.rs:10:12: 10:14 help: run `rustc --explain E0381` to see a detailed explanation
error: aborting due to 2 previous errors

worker.chan should have been initialized on the ‘worker.chan = channel()’ line. What am I missing?

thanks
jim


#2

I was able to work around this by changing the example main() to

fn main() {
  let mut worker = Worker { chan: channel() };
  let (tx, rx) = worker.chan;
}

I am still curious why the original main() way isn’t being allowed if someone knows.

thanks
jim


#3

In Rust, variables are not initialized by default, because there is no sensible default for every type.This is different from Java which uses null for any non-primitive type, and from C++, which mostly uses default constructor (but complains if there isn’t one).

So you have to provide initial value yourself. You don’t need to initialize a variable right away, but you must to make sure it is initialized before it is used. Take a look at this contrived, but valid, example:

fn foo(x: i32) -> i32 {
    let result: i32;
    let mut r = 92;
    loop {
        r = r * r + 62;
        if r == x {
            result = 1;
            break;
        }
        if r == x * x {
            result = 92;
            break;
        }
    }
    return result;
}

Here, let result: i32; declares, but does not initialize result. However we may return result after the loop ends, because Rust is able to see that we initialize it on every exit from loop. Note that result is not mut, so this = are indeed initializations, and not assignments.

If you comment result = 92;, you get an error “use of probably uninitialized variable”. If you comment out any break, you get “re-assignment of immutable variable result”.

C++ semantics of getting a default constructed type is achievable with the following snippet:

let x: MyType = Default::default()

But you’ll need to implement Default for your type.

Also, it Rust it is usually preferable to initialize variable in one step, that is

let x: MyType = some complex initialization

rather then

let mut x: MyType = default init
mutate x
mutate x
mutate x
use x

#4

And you probably want something more like

use std::sync::mpsc::{channel,Receiver,Sender};

struct Worker {
  chan: Receiver<usize>,
}

fn main() {
  let (tx, rx) = channel();
  let worker = Worker { chan: rx };
}

#5

I am curious why assigning to a field of an uninitialized struct is allowed, but reading it back isn’t. It’s like rustc is being inconsistent about whether partially initialized structs are a valid concept.


#6

Hm, I’ve missed the original error message =/ Sounds like a bug to me. Filed an issue, this is indeed a bug.