Change:
Forgetting to initialize a non-Copy variable in a loop and then referring to it gives a 'borrowed after move' error, instead of a 'possibly-uninitialized variable' error: Using a type like does result in the right error: Tested with 1.43.0 (stable) and 1.45.0-nightly.
In both Ada and D languages (in different ways, in Ada it's more refined) there's a way to tell the type system that the input variable to that bla function is not initialized and that bla must initialize it, and inside bla you can't read it before it's initialized. This means you have a way to propagate the not initialized state in function arguments, and safety is preserved. In practice this is not a big problem for Rust because in Rust you can return tuples and now Rustc is getting logic to optimize this usage pattern.
Uh, this issue is just about the error message. That this code gives an error is fine.
I'd be interested in fixing this myself, but I'm not sure where to start.
This is an issue for a command line program I'm writing. At the beginning of a function, I declare some uninitialized variables, and then I have a loop for each one. In this loop, it will read user input, and if it is valid, assign the variable that value. The loop will never complete without the variable being initialized, but rustc refuses to compile because it thinks the variable could be uninitialized after the loop.
That sounds like a different issue. This issue is about a confusing error message. The example is supposed to not compile. If the compiler rejects correct code, that's another issue.
To add context, I find this example illustrates the problem more clearly for me: the same code with the same types, just outside a loop context:

Discussion:
ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, }; use rustc_middle::ty::{self, suggest_constraining_type_param, Ty}; <del> use rustc_mir_dataflow::drop_flag_effects; use rustc_mir_dataflow::move_paths::{MoveOutIndex, MovePathIndex}; </del> <ins> use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; </ins> use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::sym; use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP};

} } <ins> let mut mpis = vec![mpi]; let move_paths = &self.move_data.move_paths; mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi)); </ins> let mut stack = Vec::new(); <del> stack.extend(predecessor_locations(self.body, location).map(|predecessor| { let is_back_edge = location.dominates(predecessor, &self.dominators); (predecessor, is_back_edge) })); </del> <ins> let mut back_edge_stack = Vec::new(); predecessor_locations(self.body, location).for_each(|predecessor| { if location.dominates(predecessor, &self.dominators) { back_edge_stack.push(predecessor) } else { stack.push(predecessor); } }); let mut reached_start = false; /* Check if the mpi is initialized as an argument */ let mut is_argument = false; for arg in self.body.args_iter() { let path = self.move_data.rev_lookup.find_local(arg); if mpis.contains(&path) { is_argument = true; } } </ins> let mut visited = FxHashSet::default(); let mut move_locations = FxHashSet::default(); let mut reinits = vec![]; let mut result = vec![]; <del> 'dfs: while let Some((location, is_back_edge)) = stack.pop() { </del> <ins> let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| { </ins> debug!( "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})", location, is_back_edge ); if !visited.insert(location) { <del> continue; </del> <ins> return true; </ins> } // check for moves

// worry about the other case: that is, if there is a move of a.b.c, it is already // marked as a move of a.b and a as well, so we will generate the correct errors // there. <del> let mut mpis = vec![mpi]; let move_paths = &self.move_data.move_paths; mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi)); </del> for moi in &self.move_data.loc_map[location] { debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi); let path = self.move_data.moves[*moi].path;

// Because we stop the DFS here, we only highlight `let c = a`, // and not `let b = a`. We will of course also report an error at // `let c = a` which highlights `let b = a` as the move. <del> continue 'dfs; </del> <ins> return true; </ins> } } } // check for inits let mut any_match = false; <del> drop_flag_effects::for_location_inits( self.infcx.tcx, &self.body, self.move_data, location, |m| { if m == mpi { any_match = true; </del> <ins> for ii in &self.move_data.init_loc_map[location] { let init = self.move_data.inits[*ii]; match init.kind { InitKind::Deep | InitKind::NonPanicPathOnly => { if mpis.contains(&init.path) { any_match = true; } </ins> } <del> }, ); </del> <ins> InitKind::Shallow => { if mpi == init.path { any_match = true; } } } } </ins> if any_match { reinits.push(location); <del> continue 'dfs; </del> <ins> return true; </ins> } <ins> return false; }; </ins> <del> stack.extend(predecessor_locations(self.body, location).map(|predecessor| { let back_edge = location.dominates(predecessor, &self.dominators); (predecessor, is_back_edge || back_edge) })); </del> <ins> while let Some(location) = stack.pop() { if dfs_iter(&mut result, location, false) { continue; } let mut has_predecessor = false; predecessor_locations(self.body, location).for_each(|predecessor| { if location.dominates(predecessor, &self.dominators) { back_edge_stack.push(predecessor) } else { stack.push(predecessor); } has_predecessor = true; }); if !has_predecessor { reached_start = true; } } if (is_argument || !reached_start) && result.is_empty() { /* Process back edges (moves in future loop iterations) only if the move path is definitely initialized upon loop entry, to avoid spurious "in previous iteration" errors. During DFS, if there's a path from the error back to the start of the function with no intervening init or move, then the move path may be uninitialized at loop entry. */ while let Some(location) = back_edge_stack.pop() { if dfs_iter(&mut result, location, true) { continue; } predecessor_locations(self.body, location) .for_each(|predecessor| back_edge_stack.push(predecessor)); } </ins> } // Check if we can reach these reinits from a move location.

<ins> // Regression test for issue #72649 // Tests that we don't emit spurious // 'value moved in previous iteration of loop' message struct NonCopy; fn good() { loop { let value = NonCopy{}; let _used = value; } } fn moved_here_1() { loop { let value = NonCopy{}; //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait let _used = value; //~^ NOTE value moved here let _used2 = value; //~ ERROR use of moved value: `value` //~^ NOTE value used here after move } } fn moved_here_2() { let value = NonCopy{}; //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait loop { let _used = value; //~^ NOTE value moved here loop { let _used2 = value; //~ ERROR use of moved value: `value` //~^ NOTE value used here after move } } } fn moved_loop_1() { let value = NonCopy{}; //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait loop { let _used = value; //~ ERROR use of moved value: `value` //~^ NOTE value moved here, in previous iteration of loop } } fn moved_loop_2() { let mut value = NonCopy{}; //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait let _used = value; value = NonCopy{}; loop { let _used2 = value; //~ ERROR use of moved value: `value` //~^ NOTE value moved here, in previous iteration of loop } } fn uninit_1() { loop { let value: NonCopy; let _used = value; //~ ERROR use of possibly-uninitialized variable: `value` //~^ NOTE use of possibly-uninitialized `value` } } fn uninit_2() { let mut value: NonCopy; loop { let _used = value; //~ ERROR use of possibly-uninitialized variable: `value` //~^ NOTE use of possibly-uninitialized `value` } } fn main() {} </ins>

<ins> error[E0382]: use of moved value: `value` --> $DIR/issue-72649-uninit-in-loop.rs:20:22 | LL |         let value = NonCopy{}; |             ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait LL | LL |         let _used = value; |                     ----- value moved here LL | LL |         let _used2 = value; |                      ^^^^^ value used here after move error[E0382]: use of moved value: `value` --> $DIR/issue-72649-uninit-in-loop.rs:32:26 | LL |     let value = NonCopy{}; |         ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait ... LL |         let _used = value; |                     ----- value moved here ... LL |             let _used2 = value; |                          ^^^^^ value used here after move error[E0382]: use of moved value: `value` --> $DIR/issue-72649-uninit-in-loop.rs:42:21 | LL |     let value = NonCopy{}; |         ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait ... LL |         let _used = value; |                     ^^^^^ value moved here, in previous iteration of loop error[E0382]: use of moved value: `value` --> $DIR/issue-72649-uninit-in-loop.rs:53:22 | LL |     let mut value = NonCopy{}; |         --------- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait ... LL |         let _used2 = value; |                      ^^^^^ value moved here, in previous iteration of loop error[E0381]: use of possibly-uninitialized variable: `value` --> $DIR/issue-72649-uninit-in-loop.rs:61:21 | LL |         let _used = value; |                     ^^^^^ use of possibly-uninitialized `value` error[E0381]: use of possibly-uninitialized variable: `value` --> $DIR/issue-72649-uninit-in-loop.rs:69:21 | LL |         let _used = value; |                     ^^^^^ use of possibly-uninitialized `value` error: aborting due to 6 previous errors Some errors have detailed explanations: E0381, E0382. For more information about an error, try `rustc --explain E0381`. </ins>