Change:
<!-- Thank you for filing a bug report! Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug. If you cannot produce a minimal reproduction case (something that would work in isolation), please provide the steps or even link to a repository that causes the problematic output to occur. --Given the following code: The current output is: <!-- The following is not always necessary. --Ideally the output should look like: or similar. The note and/or help are both subject to changes, this is just a basic idea. We could also conceivably rewrite the error message to expand on what the note says, like so: <!-- If the problem is not self-explanatory, please provide a rationale for the change. --<!-- If dramatically different output is caused by small changes, consider also adding them here. If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. The output might also be different depending on the Edition. --<!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
claim
It's hard to get 'c' in line 3 during the context where the compiler errors out the second mutable borrow message, because the mutable borrow happens between
I don't think it's critical to mention in the diagnostic. We shouldn't let the difficulty of doing that stop us from trying to improve the message. label +D-newcomer-roadblock
I'd love to try to fix this, but sadly I'm not familiar with the internals of rustc. If someone could point me towards the section where this error is, I could give it a try.
Hi ! Whenever I try to dive into fixing a problem with rustc, I usually start with a search through the codebase. In this case, I'd search for strings that come from the error message: , , or : <img width="528" alt="image" src="https://user-"And that leads me to here: So then I'd use rust-analyzer to try to find all the usages of that method. In this case, turns out there's only one: And there we see that there is already some conditional suggestion being made; it calls . And it seems there's a family of those . So I think it would make sense to try to write your own or something like that. You'll need to figure out how to sort of "back track" from the MIR back to the HIR or the AST so you can figure out whether you're inside a loop. I would study the code in to try to figure out how to do that. And don't forget to write a handful of regression tests in somewhere, maybe in : https://rustc-dev- It might also be possible to perform this check at a higher level, say, in . If you can figure out that you're in a for loop, and that the user is trying to call on the iterator, then you could raise the error then.

Discussion:
<del> use std::iter; </del> use either::Either; <ins> use hir::PatField; </ins> use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{

use rustc_span::{BytePos, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::ObligationCtxt; <ins> use std::iter; </ins> use crate::borrow_set::TwoPhaseActivation; use crate::borrowck_errors;

issued_borrow.borrowed_place, &issued_spans, ); <ins> self.explain_iterator_advancement_in_for_loop_if_applicable( &mut err, span, &issued_spans, ); </ins> err }

} } <ins> /// Suggest using `while let` for call `next` on an iterator in a for loop. /// /// For example: /// ```ignore (illustrative) /// /// for x in iter { ///     ... ///     iter.next() /// } /// ``` pub(crate) fn explain_iterator_advancement_in_for_loop_if_applicable( &self, err: &mut Diagnostic, span: Span, issued_spans: &UseSpans<'tcx>, ) { let issue_span = issued_spans.args_or_use(); let tcx = self.infcx.tcx; let hir = tcx.hir(); let Some(body_id) = hir.get(self.mir_hir_id()).body_id() else { return }; let typeck_results = tcx.typeck(self.mir_def_id()); struct ExprFinder<'hir> { issue_span: Span, expr_span: Span, body_expr: Option<&'hir hir::Expr<'hir>>, loop_bind: Option<Symbol>, } impl<'hir> Visitor<'hir> for ExprFinder<'hir> { fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) { if let hir::ExprKind::Loop(hir::Block{ stmts: [stmt, ..], ..}, _, hir::LoopSource::ForLoop, _) = ex.kind && let hir::StmtKind::Expr(hir::Expr{ kind: hir::ExprKind::Match(call, [_, bind, ..], _), ..}) = stmt.kind && let hir::ExprKind::Call(path, _args) = call.kind && let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _, _, )) = path.kind && let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind && let hir::QPath::LangItem(LangItem::OptionSome, _, _) = path && let PatField { pat: hir::Pat{ kind: hir::PatKind::Binding(_, _, ident, ..), .. }, ..} = field && self.issue_span.source_equal(call.span) { self.loop_bind = Some(ident.name); } if let hir::ExprKind::MethodCall(body_call, _recv, ..) = ex.kind && body_call.ident.name == sym::next && ex.span.source_equal(self.expr_span) { self.body_expr = Some(ex); } hir::intravisit::walk_expr(self, ex); } } let mut finder = ExprFinder { expr_span: span, issue_span, loop_bind: None, body_expr: None }; finder.visit_expr(hir.body(body_id).value); if let Some(loop_bind) = finder.loop_bind && let Some(body_expr) = finder.body_expr && let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id) && let Some(trait_did) = tcx.trait_of_item(def_id) && tcx.is_diagnostic_item(sym::Iterator, trait_did) { err.note(format!( "a for loop advances the iterator for you, the result is stored in `{}`.", loop_bind )); err.help("if you want to call `next` on a iterator within the loop, consider using `while let`."); } } </ins> /// Suggest using closure argument instead of capture. /// /// For example:

// Avoid pointing to the same function in multiple different // error messages. if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) { <ins> self.explain_iterator_advancement_in_for_loop_if_applicable( err, span, &move_spans, ); </ins> let func = tcx.def_path_str(method_did); err.subdiagnostic(CaptureReasonNote::FuncTakeSelf { func,

<ins> fn test1() { let mut chars = "Hello".chars(); for _c in chars.by_ref() { chars.next(); //~ ERROR cannot borrow `chars` as mutable more than once at a time } } fn test2() { let v = vec![1, 2, 3]; let mut iter = v.iter(); for _i in iter { iter.next(); //~ ERROR borrow of moved value: `iter` } } fn main() { } </ins>

<ins> error[E0499]: cannot borrow `chars` as mutable more than once at a time --> $DIR/issue-102972.rs:4:9 | LL |     for _c in chars.by_ref() { |               -------------- |               | |               first mutable borrow occurs here |               first borrow later used here LL |         chars.next(); |         ^^^^^^^^^^^^ second mutable borrow occurs here | = note: a for loop advances the iterator for you, the result is stored in `_c`. = help: if you want to call `next` on a iterator within the loop, consider using `while let`. error[E0382]: borrow of moved value: `iter` --> $DIR/issue-102972.rs:12:9 | LL |     let mut iter = v.iter(); |         -------- move occurs because `iter` has type `std::slice::Iter<'_, i32>`, which does not implement the `Copy` trait LL |     for _i in iter { |               ---- `iter` moved due to this implicit call to `.into_iter()` LL |         iter.next(); |         ^^^^^^^^^^^ value borrowed here after move | = note: a for loop advances the iterator for you, the result is stored in `_i`. = help: if you want to call `next` on a iterator within the loop, consider using `while let`. note: `into_iter` takes ownership of the receiver `self`, which moves `iter` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL error: aborting due to 2 previous errors Some errors have detailed explanations: E0382, E0499. For more information about an error, try `rustc --explain E0382`. </ins>