kesarling
kesarling

Reputation: 2260

How do I guarantee the rust compiler that a certain variable will get initialized in a for loop?

This question is in relation to the question asked here: How to declare a variable but not assign it?

My problem is slightly different. MRE:

let var: MyGenericType;
for value in vec_of_values_of_my_generic_type {
    // one of the values is mathematically guaranteed to meet this condition because the vector is special
    if <value meets a certain condition> {
        var = value;
    }
}

<use var>

Error:

error[E0381]: used binding `var` is possibly-uninitialized
   --> example/src/lib.rs:127:23
    |
120 |             let var: MyGenericType;
    |                 -- binding declared here but left uninitialized
...
123 |                     var = value;
    |                     -- binding initialized here in some conditions
...
127 |             foo(var);
    |                       ^^ `var` used here but it is possibly-uninitialized

How do I fix this?

Upvotes: 1

Views: 82

Answers (3)

Eugene Sh.
Eugene Sh.

Reputation: 18381

The answer to your literal question on how you can provide the compiler with a certain guarantee that it can't prove by itself - this is what unsafe {} is for. It is literally telling the compiler "Here is some code that you don't have enough information about to tell whether it is safe or not. But I have this information and can assure you that it is indeed safe!". Here, you can use a special MaybeUninit type wrapper to kind of emulate the behavior you are asking for:

use std::mem::MaybeUninit;

struct MyGenericType;


fn foo(v: MyGenericType) { unimplemented!() }
fn get_vec() -> Vec<MyGenericType> { unimplemented!() }
fn condition(v: &MyGenericType) -> bool { unimplemented!() }



fn main() {
    let vec_of_values_of_my_generic_type = get_vec();
    
    let mut var: MaybeUninit<MyGenericType> = MaybeUninit::uninit();
    for value in vec_of_values_of_my_generic_type {
        
        if condition(&value) {
            var.write(value);
        }
    }

    unsafe{ foo(var.assume_init()) };
}

Here, you are literally telling the compiler: "Assume that the variable is initialized". And since MaybeUninit::assume_init() is an unsafe function, you have no choice but wrap it in the unsafe {} block to tell the compiler that you are providing the guarantees.

(You can do similar thing with the unsafe Option::unwrap_unchecked, if you instead wrap the value into Option).

Link to playground

Upvotes: 1

kmdreko
kmdreko

Reputation: 60497

If the structure of your code means the compiler can't be sure your value is initialized, use an Option:

let mut var: Option<MyGenericType> = None;

for value in vec_of_values_of_my_generic_type {
    if <value meets a certain condition> {
        var = Some(value);
    }
}

let var = var.expect("my generic type not set");

The expect() pulls the value out of the Option and panics with that message if it was never set.

Upvotes: 0

cafce25
cafce25

Reputation: 27552

Use the right tool for the job, here that would be Iterator::find, instead of manually looping:

let var = vec_of_values_of_my_generic_type
    .into_iter()
    .find(|value| <value meets a certain condition>)
    .unwrap();

Upvotes: 2

Related Questions