Skip to content

Rustc identifies use of moved value correctly in while let loop match correctly, but the suggested fix results in infinite loop. #121466

Closed
@philjb

Description

@philjb

Code

use futures::StreamExt;

#[tokio::main]
async fn main() {
        let vec = vec!["one", "two", "three"];
        while let Some(item) = futures::stream::iter(vec).next().await {
            println!("{:?}", item);
        }
}

Current output

Compiling playground v0.0.1 (/playground)
error[E0382]: use of moved value: `vec`
 --> src/lib.rs:6:54
  |
5 |         let vec = vec!["one", "two", "three"];
  |             --- move occurs because `vec` has type `Vec<&str>`, which does not implement the `Copy` trait
6 |         while let Some(item) = futures::stream::iter(vec).next().await {
  |         ---------------------------------------------^^^--------------
  |         |                                            |
  |         |                                            value moved here, in previous iteration of loop
  |         inside of this loop
  |
help: consider cloning the value if the performance cost is acceptable
  |
6 |         while let Some(item) = futures::stream::iter(vec.clone()).next().await {
  |                                                         ++++++++

For more information about this error, try `rustc --explain E0382`.
error: could not compile `playground` (lib) due to 1 previous error

Desired output

A slightly different example:


use futures::StreamExt;

#[tokio::main]
async fn main() {
        let vec = vec!["one", "two", "three"];
        while let item = futures::stream::iter(vec).next().await {
            println!("{:?}", item);
        }
}

produces an "irrefutable loop" warning at least

Standard Error
   Compiling playground v0.0.1 (/playground)
warning: irrefutable `while let` pattern
 --> src/lib.rs:6:15
  |
6 |         while let item = futures::stream::iter(vec).next().await {
  |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: this pattern will always match, so the loop will never exit
  = help: consider instead using a `loop { ... }` with a `let` inside it
  = note: `#[warn(irrefutable_let_patterns)]` on by default

error[E0382]: use of moved value: `vec`
 --> src/lib.rs:6:48
  |
5 |         let vec = vec!["one", "two", "three"];
  |             --- move occurs because `vec` has type `Vec<&str>`, which does not implement the `Copy` trait
6 |         while let item = futures::stream::iter(vec).next().await {
  |         ---------------------------------------^^^--------------
  |         |                                      |
  |         |                                      value moved here, in previous iteration of loop
  |         inside of this loop
  |
help: consider cloning the value if the performance cost is acceptable
  |
6 |         while let item = futures::stream::iter(vec.clone()).next().await {
  |                                                   ++++++++

For more information about this error, try `rustc --explain E0382`.
warning: `playground` (lib) generated 1 warning
error: could not compile `playground` (lib) due to 1 previous error; 1 warning emitted
Standard Output

Rationale and extra context

I'm a rust beginner: When I mistakenly wrote code matching irrefutable_let_patterns the warning was helpful to me that I had made a mistake.

But I still had a flaw in that I should have made the stream outside the while let match stanza - The code is re-initializain the loop variable on each iteration. The rustc suggestion to add a clone() on vec as vec was moved in the previous loop iteration is "technically correct" but the suggestion creates another irrefutable loop which doesn't produce such a warning. I know this pattern is harder to recognize as it involves the r-value instead of the match side (while let x =).

while let Some(item) = futures::stream::iter(vec.clone()).next().await

Since I'm a beginner I didn't realize that when rustc said value moved here, in previous iteration of loop that I had put use of vec in the wrong place. It should have been.

let mut s = futures::stream::iter(vec);
while let Some(item) = s.next().await {
...

While I ran into this when using an async stream, I think it a general situation whenever the right hand side creates a new and same object on the right side. Here's an example with an iterator along.

fn main() {
        let vec = vec!["one", "two", "three"];
        while let Some(item) = vec.iter().next() {
            println!("{:?}", item);
        }
}

It's a problem in the class of loop variable issues. I come from golang which says a similar issue that everone hits once or twice (and hopefully recognizes when!). Goland is introducing a way to systematically addres this class of error: https://go.dev/blog/loopvar-preview

I was encourage to report this by @carols10cents.

Other cases

No response

Rust Version

1.76.0 - I used the playground to reproduce it.

Anything else?

No response

Metadata

Metadata

Assignees

Labels

A-diagnosticsArea: Messages for errors, warnings, and lintsD-incorrectDiagnostics: A diagnostic that is giving misleading or incorrect information.D-newcomer-roadblockDiagnostics: Confusing error or lint; hard to understand for new users.S-has-mcveStatus: A Minimal Complete and Verifiable Example has been found for this issueT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions