I'm relatively new to Rust. This question turned out to be pretty long so I'll start with the bottom-line: Which solution do you prefer? Do you have any ideas or remarks?
My code does not compile because lines lines 6 (prev = curr) and 12 (bar(...)) use variables which the compiler suspects are possibly uninitialized. As the programmer, I know that there is no reason to worry because lines 6 and 12 do not run during the first iteration.
let mut curr: Enum;
let mut prev: Enum;
for i in 0..10 {
if i > 0 {
prev = curr;
}
curr = foo();
if i > 0 {
bar(&curr, &prev);
}
}
I understand that there's a limit to what you can expect from a compiler to know. So I came up with 3 different ways to answer the language's safety restriction.
1) Initialize and stop thinking too much
I could just initialize the variables with arbitrary values. The risk is that a maintainer might mistakenly think that those initial, hopefully unused, values have some important meaning. Lines 1-2 would become:
let mut curr: Enum = Enum::RED; // Just an arbitrary value!
let mut prev: Enum = Enum::BLUE; // Just an arbitrary value!
2) Add None value to Enum
Lines 1-2 would become
let mut curr: Enum = Enum::None;
let mut prev: Enum = Enum::None;
The reason that I dislike this solution is that now I've added a new possible and unnatrural value to my enum. For my own safety, I will have to add assertion checks and match-branches to foo() and bar(), and any other function that uses Enum.
3) Option<Enum>
I think that this solution is the most "by-the-book" one, but it makes the code longer and harder to comprehend.
Lines 1-2 would become
let mut curr: Option<Enum> = None;
let mut prev: Option<Enum> = None;
This time, None belongs to Option and not to Enum. The innocent prev = curr statement would become
prev = match curr {
Some(_) => curr,
None => panic!("Ah!")
};
and the naive call to bar() would uglify to
match prev {
Some(_) => bar(&curr, &prev),
None => panic!("Oy!")
};
Questions: Which solution do you prefer? Do you have any ideas or remarks?