Reason for 'Borrow of uninitialised variable'?

Why doesn't this code compile? Isn't the struct beta in the vector well and truly initialised?

#![allow(non_camel_case_types)]

#[derive(PartialEq, Eq, Clone, Copy)]
enum alpha {
    one,
    two,
    three,
}

struct beta {
    number: alpha,
    no: i32,
}

fn main() {
    let mut vector: Vec<beta> = Vec::new();
    let mut number: alpha;
    vector.push(beta {
        number: alpha::one,
        no: 2,
    });
    if vector.iter().any(|i| {
        if i.no == 2 {
            number = i.number.clone();
            true
        } else {
            false
        }
    }) {
        println!("D");
    }
}

The error I get is

error[E0381]: borrow of possibly-uninitialized variable: `number`
  --> main.rs:19:27
   |
19 |      if vector.iter().any(|i| {
   |                           ^^^ use of possibly-uninitialized `number`
20 |             if i.no == 2 {
21 |                 number = i.number.clone();
   |                 ------ borrow occurs due to use in closure

error: aborting due to previous error

For more information about this error, try `rustc --explain E0381`.

I think the problem is generating the closure |i| { /* ... */ } when number is still uninitialized. That requires capturing a reference to the uninitialized variable, which Rust really doesn’t like to do.

You're right, my friend. Thanks for the help. I still don't see what the uninitialised state of the number variable has to do with being safe. What difference does it make if I use uninitialised variable within a closure (after giving it a value, of course)?

This compiles, making the changes you suggested.

 #![allow(non_camel_case_types)]

#[derive(PartialEq, Eq, Clone, Copy)]
enum alpha {
    one,
    two,
    three,
}

struct beta {
    number: alpha,
    no: i32,
}

fn main() {
    let mut vector: Vec<beta> = Vec::new();
    let mut number: alpha = alpha::one;
    vector.push(beta {
        number: alpha::one,
        no: 2,
    });
    if vector.iter().any(|i| {
        if i.no == 2 {
            number = i.number.clone();
            true
        } else {
            false
        }
    }) {
        println!("D");
    }
}

but this does too, in relevance to the question above.

#![allow(non_camel_case_types)]

#[derive(PartialEq, Eq, Clone, Copy)]
enum alpha {
    one,
    two,
    three,
}

struct beta {
    number: alpha,
    no: i32,
}

fn main() {
    let mut vector: Vec<beta> = Vec::new();
    let mut number: alpha = alpha::one;
    vector.push(beta {
        number: alpha::one,
        no: 2,
    });
    number = vector[0].number;
}

This is one of the edge cases where what you want to do is safe in practice, but not something the compiler is currently able to prove. The compiler considers function boundaries, including for closures, as opaque when it’s safety-checking: your closure requires a reference to an uninitialized variable, which isn’t something the compiler allows— the fact that you aren’t reading from the reference isn’t really considered.

The more idiomatic way to do this is to return the value from the closure:

    if let Some(number) = vector.iter()
                                .filter(|i| i.no == 2)
                                .map(|i| i.number.clone())
                                .next()
    {
        println!("D");
    }
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.